diff --git a/libs/zlib/vc100.pdb b/libs/zlib/vc100.pdb new file mode 100644 index 0000000..d9c401e Binary files /dev/null and b/libs/zlib/vc100.pdb differ diff --git a/libs/zlib/zconf.h b/libs/zlib/zconf.h new file mode 100644 index 0000000..9987a77 --- /dev/null +++ b/libs/zlib/zconf.h @@ -0,0 +1,511 @@ +/* zconf.h -- configuration of the zlib compression library + * Copyright (C) 1995-2013 Jean-loup Gailly. + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* @(#) $Id$ */ + +#ifndef ZCONF_H +#define ZCONF_H + +/* + * If you *really* need a unique prefix for all types and library functions, + * compile with -DZ_PREFIX. The "standard" zlib should be compiled without it. + * Even better than compiling with -DZ_PREFIX would be to use configure to set + * this permanently in zconf.h using "./configure --zprefix". + */ +#ifdef Z_PREFIX /* may be set to #if 1 by ./configure */ +# define Z_PREFIX_SET + +/* all linked symbols */ +# define _dist_code z__dist_code +# define _length_code z__length_code +# define _tr_align z__tr_align +# define _tr_flush_bits z__tr_flush_bits +# define _tr_flush_block z__tr_flush_block +# define _tr_init z__tr_init +# define _tr_stored_block z__tr_stored_block +# define _tr_tally z__tr_tally +# define adler32 z_adler32 +# define adler32_combine z_adler32_combine +# define adler32_combine64 z_adler32_combine64 +# ifndef Z_SOLO +# define compress z_compress +# define compress2 z_compress2 +# define compressBound z_compressBound +# endif +# define crc32 z_crc32 +# define crc32_combine z_crc32_combine +# define crc32_combine64 z_crc32_combine64 +# define deflate z_deflate +# define deflateBound z_deflateBound +# define deflateCopy z_deflateCopy +# define deflateEnd z_deflateEnd +# define deflateInit2_ z_deflateInit2_ +# define deflateInit_ z_deflateInit_ +# define deflateParams z_deflateParams +# define deflatePending z_deflatePending +# define deflatePrime z_deflatePrime +# define deflateReset z_deflateReset +# define deflateResetKeep z_deflateResetKeep +# define deflateSetDictionary z_deflateSetDictionary +# define deflateSetHeader z_deflateSetHeader +# define deflateTune z_deflateTune +# define deflate_copyright z_deflate_copyright +# define get_crc_table z_get_crc_table +# ifndef Z_SOLO +# define gz_error z_gz_error +# define gz_intmax z_gz_intmax +# define gz_strwinerror z_gz_strwinerror +# define gzbuffer z_gzbuffer +# define gzclearerr z_gzclearerr +# define gzclose z_gzclose +# define gzclose_r z_gzclose_r +# define gzclose_w z_gzclose_w +# define gzdirect z_gzdirect +# define gzdopen z_gzdopen +# define gzeof z_gzeof +# define gzerror z_gzerror +# define gzflush z_gzflush +# define gzgetc z_gzgetc +# define gzgetc_ z_gzgetc_ +# define gzgets z_gzgets +# define gzoffset z_gzoffset +# define gzoffset64 z_gzoffset64 +# define gzopen z_gzopen +# define gzopen64 z_gzopen64 +# ifdef _WIN32 +# define gzopen_w z_gzopen_w +# endif +# define gzprintf z_gzprintf +# define gzvprintf z_gzvprintf +# define gzputc z_gzputc +# define gzputs z_gzputs +# define gzread z_gzread +# define gzrewind z_gzrewind +# define gzseek z_gzseek +# define gzseek64 z_gzseek64 +# define gzsetparams z_gzsetparams +# define gztell z_gztell +# define gztell64 z_gztell64 +# define gzungetc z_gzungetc +# define gzwrite z_gzwrite +# endif +# define inflate z_inflate +# define inflateBack z_inflateBack +# define inflateBackEnd z_inflateBackEnd +# define inflateBackInit_ z_inflateBackInit_ +# define inflateCopy z_inflateCopy +# define inflateEnd z_inflateEnd +# define inflateGetHeader z_inflateGetHeader +# define inflateInit2_ z_inflateInit2_ +# define inflateInit_ z_inflateInit_ +# define inflateMark z_inflateMark +# define inflatePrime z_inflatePrime +# define inflateReset z_inflateReset +# define inflateReset2 z_inflateReset2 +# define inflateSetDictionary z_inflateSetDictionary +# define inflateGetDictionary z_inflateGetDictionary +# define inflateSync z_inflateSync +# define inflateSyncPoint z_inflateSyncPoint +# define inflateUndermine z_inflateUndermine +# define inflateResetKeep z_inflateResetKeep +# define inflate_copyright z_inflate_copyright +# define inflate_fast z_inflate_fast +# define inflate_table z_inflate_table +# ifndef Z_SOLO +# define uncompress z_uncompress +# endif +# define zError z_zError +# ifndef Z_SOLO +# define zcalloc z_zcalloc +# define zcfree z_zcfree +# endif +# define zlibCompileFlags z_zlibCompileFlags +# define zlibVersion z_zlibVersion + +/* all zlib typedefs in zlib.h and zconf.h */ +# define Byte z_Byte +# define Bytef z_Bytef +# define alloc_func z_alloc_func +# define charf z_charf +# define free_func z_free_func +# ifndef Z_SOLO +# define gzFile z_gzFile +# endif +# define gz_header z_gz_header +# define gz_headerp z_gz_headerp +# define in_func z_in_func +# define intf z_intf +# define out_func z_out_func +# define uInt z_uInt +# define uIntf z_uIntf +# define uLong z_uLong +# define uLongf z_uLongf +# define voidp z_voidp +# define voidpc z_voidpc +# define voidpf z_voidpf + +/* all zlib structs in zlib.h and zconf.h */ +# define gz_header_s z_gz_header_s +# define internal_state z_internal_state + +#endif + +#if defined(__MSDOS__) && !defined(MSDOS) +# define MSDOS +#endif +#if (defined(OS_2) || defined(__OS2__)) && !defined(OS2) +# define OS2 +#endif +#if defined(_WINDOWS) && !defined(WINDOWS) +# define WINDOWS +#endif +#if defined(_WIN32) || defined(_WIN32_WCE) || defined(__WIN32__) +# ifndef WIN32 +# define WIN32 +# endif +#endif +#if (defined(MSDOS) || defined(OS2) || defined(WINDOWS)) && !defined(WIN32) +# if !defined(__GNUC__) && !defined(__FLAT__) && !defined(__386__) +# ifndef SYS16BIT +# define SYS16BIT +# endif +# endif +#endif + +/* + * Compile with -DMAXSEG_64K if the alloc function cannot allocate more + * than 64k bytes at a time (needed on systems with 16-bit int). + */ +#ifdef SYS16BIT +# define MAXSEG_64K +#endif +#ifdef MSDOS +# define UNALIGNED_OK +#endif + +#ifdef __STDC_VERSION__ +# ifndef STDC +# define STDC +# endif +# if __STDC_VERSION__ >= 199901L +# ifndef STDC99 +# define STDC99 +# endif +# endif +#endif +#if !defined(STDC) && (defined(__STDC__) || defined(__cplusplus)) +# define STDC +#endif +#if !defined(STDC) && (defined(__GNUC__) || defined(__BORLANDC__)) +# define STDC +#endif +#if !defined(STDC) && (defined(MSDOS) || defined(WINDOWS) || defined(WIN32)) +# define STDC +#endif +#if !defined(STDC) && (defined(OS2) || defined(__HOS_AIX__)) +# define STDC +#endif + +#if defined(__OS400__) && !defined(STDC) /* iSeries (formerly AS/400). */ +# define STDC +#endif + +#ifndef STDC +# ifndef const /* cannot use !defined(STDC) && !defined(const) on Mac */ +# define const /* note: need a more gentle solution here */ +# endif +#endif + +#if defined(ZLIB_CONST) && !defined(z_const) +# define z_const const +#else +# define z_const +#endif + +/* Some Mac compilers merge all .h files incorrectly: */ +#if defined(__MWERKS__)||defined(applec)||defined(THINK_C)||defined(__SC__) +# define NO_DUMMY_DECL +#endif + +/* Maximum value for memLevel in deflateInit2 */ +#ifndef MAX_MEM_LEVEL +# ifdef MAXSEG_64K +# define MAX_MEM_LEVEL 8 +# else +# define MAX_MEM_LEVEL 9 +# endif +#endif + +/* Maximum value for windowBits in deflateInit2 and inflateInit2. + * WARNING: reducing MAX_WBITS makes minigzip unable to extract .gz files + * created by gzip. (Files created by minigzip can still be extracted by + * gzip.) + */ +#ifndef MAX_WBITS +# define MAX_WBITS 15 /* 32K LZ77 window */ +#endif + +/* The memory requirements for deflate are (in bytes): + (1 << (windowBits+2)) + (1 << (memLevel+9)) + that is: 128K for windowBits=15 + 128K for memLevel = 8 (default values) + plus a few kilobytes for small objects. For example, if you want to reduce + the default memory requirements from 256K to 128K, compile with + make CFLAGS="-O -DMAX_WBITS=14 -DMAX_MEM_LEVEL=7" + Of course this will generally degrade compression (there's no free lunch). + + The memory requirements for inflate are (in bytes) 1 << windowBits + that is, 32K for windowBits=15 (default value) plus a few kilobytes + for small objects. +*/ + + /* Type declarations */ + +#ifndef OF /* function prototypes */ +# ifdef STDC +# define OF(args) args +# else +# define OF(args) () +# endif +#endif + +#ifndef Z_ARG /* function prototypes for stdarg */ +# if defined(STDC) || defined(Z_HAVE_STDARG_H) +# define Z_ARG(args) args +# else +# define Z_ARG(args) () +# endif +#endif + +/* The following definitions for FAR are needed only for MSDOS mixed + * model programming (small or medium model with some far allocations). + * This was tested only with MSC; for other MSDOS compilers you may have + * to define NO_MEMCPY in zutil.h. If you don't need the mixed model, + * just define FAR to be empty. + */ +#ifdef SYS16BIT +# if defined(M_I86SM) || defined(M_I86MM) + /* MSC small or medium model */ +# define SMALL_MEDIUM +# ifdef _MSC_VER +# define FAR _far +# else +# define FAR far +# endif +# endif +# if (defined(__SMALL__) || defined(__MEDIUM__)) + /* Turbo C small or medium model */ +# define SMALL_MEDIUM +# ifdef __BORLANDC__ +# define FAR _far +# else +# define FAR far +# endif +# endif +#endif + +#if defined(WINDOWS) || defined(WIN32) + /* If building or using zlib as a DLL, define ZLIB_DLL. + * This is not mandatory, but it offers a little performance increase. + */ +# ifdef ZLIB_DLL +# if defined(WIN32) && (!defined(__BORLANDC__) || (__BORLANDC__ >= 0x500)) +# ifdef ZLIB_INTERNAL +# define ZEXTERN extern __declspec(dllexport) +# else +# define ZEXTERN extern __declspec(dllimport) +# endif +# endif +# endif /* ZLIB_DLL */ + /* If building or using zlib with the WINAPI/WINAPIV calling convention, + * define ZLIB_WINAPI. + * Caution: the standard ZLIB1.DLL is NOT compiled using ZLIB_WINAPI. + */ +# ifdef ZLIB_WINAPI +# ifdef FAR +# undef FAR +# endif +# include + /* No need for _export, use ZLIB.DEF instead. */ + /* For complete Windows compatibility, use WINAPI, not __stdcall. */ +# define ZEXPORT WINAPI +# ifdef WIN32 +# define ZEXPORTVA WINAPIV +# else +# define ZEXPORTVA FAR CDECL +# endif +# endif +#endif + +#if defined (__BEOS__) +# ifdef ZLIB_DLL +# ifdef ZLIB_INTERNAL +# define ZEXPORT __declspec(dllexport) +# define ZEXPORTVA __declspec(dllexport) +# else +# define ZEXPORT __declspec(dllimport) +# define ZEXPORTVA __declspec(dllimport) +# endif +# endif +#endif + +#ifndef ZEXTERN +# define ZEXTERN extern +#endif +#ifndef ZEXPORT +# define ZEXPORT +#endif +#ifndef ZEXPORTVA +# define ZEXPORTVA +#endif + +#ifndef FAR +# define FAR +#endif + +#if !defined(__MACTYPES__) +typedef unsigned char Byte; /* 8 bits */ +#endif +typedef unsigned int uInt; /* 16 bits or more */ +typedef unsigned long uLong; /* 32 bits or more */ + +#ifdef SMALL_MEDIUM + /* Borland C/C++ and some old MSC versions ignore FAR inside typedef */ +# define Bytef Byte FAR +#else + typedef Byte FAR Bytef; +#endif +typedef char FAR charf; +typedef int FAR intf; +typedef uInt FAR uIntf; +typedef uLong FAR uLongf; + +#ifdef STDC + typedef void const *voidpc; + typedef void FAR *voidpf; + typedef void *voidp; +#else + typedef Byte const *voidpc; + typedef Byte FAR *voidpf; + typedef Byte *voidp; +#endif + +#if !defined(Z_U4) && !defined(Z_SOLO) && defined(STDC) +# include +# if (UINT_MAX == 0xffffffffUL) +# define Z_U4 unsigned +# elif (ULONG_MAX == 0xffffffffUL) +# define Z_U4 unsigned long +# elif (USHRT_MAX == 0xffffffffUL) +# define Z_U4 unsigned short +# endif +#endif + +#ifdef Z_U4 + typedef Z_U4 z_crc_t; +#else + typedef unsigned long z_crc_t; +#endif + +#ifdef HAVE_UNISTD_H /* may be set to #if 1 by ./configure */ +# define Z_HAVE_UNISTD_H +#endif + +#ifdef HAVE_STDARG_H /* may be set to #if 1 by ./configure */ +# define Z_HAVE_STDARG_H +#endif + +#ifdef STDC +# ifndef Z_SOLO +# include /* for off_t */ +# endif +#endif + +#if defined(STDC) || defined(Z_HAVE_STDARG_H) +# ifndef Z_SOLO +# include /* for va_list */ +# endif +#endif + +#ifdef _WIN32 +# ifndef Z_SOLO +# include /* for wchar_t */ +# endif +#endif + +/* a little trick to accommodate both "#define _LARGEFILE64_SOURCE" and + * "#define _LARGEFILE64_SOURCE 1" as requesting 64-bit operations, (even + * though the former does not conform to the LFS document), but considering + * both "#undef _LARGEFILE64_SOURCE" and "#define _LARGEFILE64_SOURCE 0" as + * equivalently requesting no 64-bit operations + */ +#if defined(_LARGEFILE64_SOURCE) && -_LARGEFILE64_SOURCE - -1 == 1 +# undef _LARGEFILE64_SOURCE +#endif + +#if defined(__WATCOMC__) && !defined(Z_HAVE_UNISTD_H) +# define Z_HAVE_UNISTD_H +#endif +#ifndef Z_SOLO +# if defined(Z_HAVE_UNISTD_H) || defined(_LARGEFILE64_SOURCE) +# include /* for SEEK_*, off_t, and _LFS64_LARGEFILE */ +# ifdef VMS +# include /* for off_t */ +# endif +# ifndef z_off_t +# define z_off_t off_t +# endif +# endif +#endif + +#if defined(_LFS64_LARGEFILE) && _LFS64_LARGEFILE-0 +# define Z_LFS64 +#endif + +#if defined(_LARGEFILE64_SOURCE) && defined(Z_LFS64) +# define Z_LARGE64 +#endif + +#if defined(_FILE_OFFSET_BITS) && _FILE_OFFSET_BITS-0 == 64 && defined(Z_LFS64) +# define Z_WANT64 +#endif + +#if !defined(SEEK_SET) && !defined(Z_SOLO) +# define SEEK_SET 0 /* Seek from beginning of file. */ +# define SEEK_CUR 1 /* Seek from current position. */ +# define SEEK_END 2 /* Set file pointer to EOF plus "offset" */ +#endif + +#ifndef z_off_t +# define z_off_t long +#endif + +#if !defined(_WIN32) && defined(Z_LARGE64) +# define z_off64_t off64_t +#else +# if defined(_WIN32) && !defined(__GNUC__) && !defined(Z_SOLO) +# define z_off64_t __int64 +# else +# define z_off64_t z_off_t +# endif +#endif + +/* MVS linker does not support external names larger than 8 bytes */ +#if defined(__MVS__) + #pragma map(deflateInit_,"DEIN") + #pragma map(deflateInit2_,"DEIN2") + #pragma map(deflateEnd,"DEEND") + #pragma map(deflateBound,"DEBND") + #pragma map(inflateInit_,"ININ") + #pragma map(inflateInit2_,"ININ2") + #pragma map(inflateEnd,"INEND") + #pragma map(inflateSync,"INSY") + #pragma map(inflateSetDictionary,"INSEDI") + #pragma map(compressBound,"CMBND") + #pragma map(inflate_table,"INTABL") + #pragma map(inflate_fast,"INFA") + #pragma map(inflate_copyright,"INCOPY") +#endif + +#endif /* ZCONF_H */ diff --git a/libs/zlib/zlib.h b/libs/zlib/zlib.h new file mode 100644 index 0000000..3e0c767 --- /dev/null +++ b/libs/zlib/zlib.h @@ -0,0 +1,1768 @@ +/* zlib.h -- interface of the 'zlib' general purpose compression library + version 1.2.8, April 28th, 2013 + + Copyright (C) 1995-2013 Jean-loup Gailly and Mark Adler + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + Jean-loup Gailly Mark Adler + jloup@gzip.org madler@alumni.caltech.edu + + + The data format used by the zlib library is described by RFCs (Request for + Comments) 1950 to 1952 in the files http://tools.ietf.org/html/rfc1950 + (zlib format), rfc1951 (deflate format) and rfc1952 (gzip format). +*/ + +#ifndef ZLIB_H +#define ZLIB_H + +#include "zconf.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define ZLIB_VERSION "1.2.8" +#define ZLIB_VERNUM 0x1280 +#define ZLIB_VER_MAJOR 1 +#define ZLIB_VER_MINOR 2 +#define ZLIB_VER_REVISION 8 +#define ZLIB_VER_SUBREVISION 0 + +/* + The 'zlib' compression library provides in-memory compression and + decompression functions, including integrity checks of the uncompressed data. + This version of the library supports only one compression method (deflation) + but other algorithms will be added later and will have the same stream + interface. + + Compression can be done in a single step if the buffers are large enough, + or can be done by repeated calls of the compression function. In the latter + case, the application must provide more input and/or consume the output + (providing more output space) before each call. + + The compressed data format used by default by the in-memory functions is + the zlib format, which is a zlib wrapper documented in RFC 1950, wrapped + around a deflate stream, which is itself documented in RFC 1951. + + The library also supports reading and writing files in gzip (.gz) format + with an interface similar to that of stdio using the functions that start + with "gz". The gzip format is different from the zlib format. gzip is a + gzip wrapper, documented in RFC 1952, wrapped around a deflate stream. + + This library can optionally read and write gzip streams in memory as well. + + The zlib format was designed to be compact and fast for use in memory + and on communications channels. The gzip format was designed for single- + file compression on file systems, has a larger header than zlib to maintain + directory information, and uses a different, slower check method than zlib. + + The library does not install any signal handler. The decoder checks + the consistency of the compressed data, so the library should never crash + even in case of corrupted input. +*/ + +typedef voidpf (*alloc_func) OF((voidpf opaque, uInt items, uInt size)); +typedef void (*free_func) OF((voidpf opaque, voidpf address)); + +struct internal_state; + +typedef struct z_stream_s { + z_const Bytef *next_in; /* next input byte */ + uInt avail_in; /* number of bytes available at next_in */ + uLong total_in; /* total number of input bytes read so far */ + + Bytef *next_out; /* next output byte should be put there */ + uInt avail_out; /* remaining free space at next_out */ + uLong total_out; /* total number of bytes output so far */ + + z_const char *msg; /* last error message, NULL if no error */ + struct internal_state FAR *state; /* not visible by applications */ + + alloc_func zalloc; /* used to allocate the internal state */ + free_func zfree; /* used to free the internal state */ + voidpf opaque; /* private data object passed to zalloc and zfree */ + + int data_type; /* best guess about the data type: binary or text */ + uLong adler; /* adler32 value of the uncompressed data */ + uLong reserved; /* reserved for future use */ +} z_stream; + +typedef z_stream FAR *z_streamp; + +/* + gzip header information passed to and from zlib routines. See RFC 1952 + for more details on the meanings of these fields. +*/ +typedef struct gz_header_s { + int text; /* true if compressed data believed to be text */ + uLong time; /* modification time */ + int xflags; /* extra flags (not used when writing a gzip file) */ + int os; /* operating system */ + Bytef *extra; /* pointer to extra field or Z_NULL if none */ + uInt extra_len; /* extra field length (valid if extra != Z_NULL) */ + uInt extra_max; /* space at extra (only when reading header) */ + Bytef *name; /* pointer to zero-terminated file name or Z_NULL */ + uInt name_max; /* space at name (only when reading header) */ + Bytef *comment; /* pointer to zero-terminated comment or Z_NULL */ + uInt comm_max; /* space at comment (only when reading header) */ + int hcrc; /* true if there was or will be a header crc */ + int done; /* true when done reading gzip header (not used + when writing a gzip file) */ +} gz_header; + +typedef gz_header FAR *gz_headerp; + +/* + The application must update next_in and avail_in when avail_in has dropped + to zero. It must update next_out and avail_out when avail_out has dropped + to zero. The application must initialize zalloc, zfree and opaque before + calling the init function. All other fields are set by the compression + library and must not be updated by the application. + + The opaque value provided by the application will be passed as the first + parameter for calls of zalloc and zfree. This can be useful for custom + memory management. The compression library attaches no meaning to the + opaque value. + + zalloc must return Z_NULL if there is not enough memory for the object. + If zlib is used in a multi-threaded application, zalloc and zfree must be + thread safe. + + On 16-bit systems, the functions zalloc and zfree must be able to allocate + exactly 65536 bytes, but will not be required to allocate more than this if + the symbol MAXSEG_64K is defined (see zconf.h). WARNING: On MSDOS, pointers + returned by zalloc for objects of exactly 65536 bytes *must* have their + offset normalized to zero. The default allocation function provided by this + library ensures this (see zutil.c). To reduce memory requirements and avoid + any allocation of 64K objects, at the expense of compression ratio, compile + the library with -DMAX_WBITS=14 (see zconf.h). + + The fields total_in and total_out can be used for statistics or progress + reports. After compression, total_in holds the total size of the + uncompressed data and may be saved for use in the decompressor (particularly + if the decompressor wants to decompress everything in a single step). +*/ + + /* constants */ + +#define Z_NO_FLUSH 0 +#define Z_PARTIAL_FLUSH 1 +#define Z_SYNC_FLUSH 2 +#define Z_FULL_FLUSH 3 +#define Z_FINISH 4 +#define Z_BLOCK 5 +#define Z_TREES 6 +/* Allowed flush values; see deflate() and inflate() below for details */ + +#define Z_OK 0 +#define Z_STREAM_END 1 +#define Z_NEED_DICT 2 +#define Z_ERRNO (-1) +#define Z_STREAM_ERROR (-2) +#define Z_DATA_ERROR (-3) +#define Z_MEM_ERROR (-4) +#define Z_BUF_ERROR (-5) +#define Z_VERSION_ERROR (-6) +/* Return codes for the compression/decompression functions. Negative values + * are errors, positive values are used for special but normal events. + */ + +#define Z_NO_COMPRESSION 0 +#define Z_BEST_SPEED 1 +#define Z_BEST_COMPRESSION 9 +#define Z_DEFAULT_COMPRESSION (-1) +/* compression levels */ + +#define Z_FILTERED 1 +#define Z_HUFFMAN_ONLY 2 +#define Z_RLE 3 +#define Z_FIXED 4 +#define Z_DEFAULT_STRATEGY 0 +/* compression strategy; see deflateInit2() below for details */ + +#define Z_BINARY 0 +#define Z_TEXT 1 +#define Z_ASCII Z_TEXT /* for compatibility with 1.2.2 and earlier */ +#define Z_UNKNOWN 2 +/* Possible values of the data_type field (though see inflate()) */ + +#define Z_DEFLATED 8 +/* The deflate compression method (the only one supported in this version) */ + +#define Z_NULL 0 /* for initializing zalloc, zfree, opaque */ + +#define zlib_version zlibVersion() +/* for compatibility with versions < 1.0.2 */ + + + /* basic functions */ + +ZEXTERN const char * ZEXPORT zlibVersion OF((void)); +/* The application can compare zlibVersion and ZLIB_VERSION for consistency. + If the first character differs, the library code actually used is not + compatible with the zlib.h header file used by the application. This check + is automatically made by deflateInit and inflateInit. + */ + +/* +ZEXTERN int ZEXPORT deflateInit OF((z_streamp strm, int level)); + + Initializes the internal stream state for compression. The fields + zalloc, zfree and opaque must be initialized before by the caller. If + zalloc and zfree are set to Z_NULL, deflateInit updates them to use default + allocation functions. + + The compression level must be Z_DEFAULT_COMPRESSION, or between 0 and 9: + 1 gives best speed, 9 gives best compression, 0 gives no compression at all + (the input data is simply copied a block at a time). Z_DEFAULT_COMPRESSION + requests a default compromise between speed and compression (currently + equivalent to level 6). + + deflateInit returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_STREAM_ERROR if level is not a valid compression level, or + Z_VERSION_ERROR if the zlib library version (zlib_version) is incompatible + with the version assumed by the caller (ZLIB_VERSION). msg is set to null + if there is no error message. deflateInit does not perform any compression: + this will be done by deflate(). +*/ + + +ZEXTERN int ZEXPORT deflate OF((z_streamp strm, int flush)); +/* + deflate compresses as much data as possible, and stops when the input + buffer becomes empty or the output buffer becomes full. It may introduce + some output latency (reading input without producing any output) except when + forced to flush. + + The detailed semantics are as follows. deflate performs one or both of the + following actions: + + - Compress more input starting at next_in and update next_in and avail_in + accordingly. If not all input can be processed (because there is not + enough room in the output buffer), next_in and avail_in are updated and + processing will resume at this point for the next call of deflate(). + + - Provide more output starting at next_out and update next_out and avail_out + accordingly. This action is forced if the parameter flush is non zero. + Forcing flush frequently degrades the compression ratio, so this parameter + should be set only when necessary (in interactive applications). Some + output may be provided even if flush is not set. + + Before the call of deflate(), the application should ensure that at least + one of the actions is possible, by providing more input and/or consuming more + output, and updating avail_in or avail_out accordingly; avail_out should + never be zero before the call. The application can consume the compressed + output when it wants, for example when the output buffer is full (avail_out + == 0), or after each call of deflate(). If deflate returns Z_OK and with + zero avail_out, it must be called again after making room in the output + buffer because there might be more output pending. + + Normally the parameter flush is set to Z_NO_FLUSH, which allows deflate to + decide how much data to accumulate before producing output, in order to + maximize compression. + + If the parameter flush is set to Z_SYNC_FLUSH, all pending output is + flushed to the output buffer and the output is aligned on a byte boundary, so + that the decompressor can get all input data available so far. (In + particular avail_in is zero after the call if enough output space has been + provided before the call.) Flushing may degrade compression for some + compression algorithms and so it should be used only when necessary. This + completes the current deflate block and follows it with an empty stored block + that is three bits plus filler bits to the next byte, followed by four bytes + (00 00 ff ff). + + If flush is set to Z_PARTIAL_FLUSH, all pending output is flushed to the + output buffer, but the output is not aligned to a byte boundary. All of the + input data so far will be available to the decompressor, as for Z_SYNC_FLUSH. + This completes the current deflate block and follows it with an empty fixed + codes block that is 10 bits long. This assures that enough bytes are output + in order for the decompressor to finish the block before the empty fixed code + block. + + If flush is set to Z_BLOCK, a deflate block is completed and emitted, as + for Z_SYNC_FLUSH, but the output is not aligned on a byte boundary, and up to + seven bits of the current block are held to be written as the next byte after + the next deflate block is completed. In this case, the decompressor may not + be provided enough bits at this point in order to complete decompression of + the data provided so far to the compressor. It may need to wait for the next + block to be emitted. This is for advanced applications that need to control + the emission of deflate blocks. + + If flush is set to Z_FULL_FLUSH, all output is flushed as with + Z_SYNC_FLUSH, and the compression state is reset so that decompression can + restart from this point if previous compressed data has been damaged or if + random access is desired. Using Z_FULL_FLUSH too often can seriously degrade + compression. + + If deflate returns with avail_out == 0, this function must be called again + with the same value of the flush parameter and more output space (updated + avail_out), until the flush is complete (deflate returns with non-zero + avail_out). In the case of a Z_FULL_FLUSH or Z_SYNC_FLUSH, make sure that + avail_out is greater than six to avoid repeated flush markers due to + avail_out == 0 on return. + + If the parameter flush is set to Z_FINISH, pending input is processed, + pending output is flushed and deflate returns with Z_STREAM_END if there was + enough output space; if deflate returns with Z_OK, this function must be + called again with Z_FINISH and more output space (updated avail_out) but no + more input data, until it returns with Z_STREAM_END or an error. After + deflate has returned Z_STREAM_END, the only possible operations on the stream + are deflateReset or deflateEnd. + + Z_FINISH can be used immediately after deflateInit if all the compression + is to be done in a single step. In this case, avail_out must be at least the + value returned by deflateBound (see below). Then deflate is guaranteed to + return Z_STREAM_END. If not enough output space is provided, deflate will + not return Z_STREAM_END, and it must be called again as described above. + + deflate() sets strm->adler to the adler32 checksum of all input read + so far (that is, total_in bytes). + + deflate() may update strm->data_type if it can make a good guess about + the input data type (Z_BINARY or Z_TEXT). In doubt, the data is considered + binary. This field is only for information purposes and does not affect the + compression algorithm in any manner. + + deflate() returns Z_OK if some progress has been made (more input + processed or more output produced), Z_STREAM_END if all input has been + consumed and all output has been produced (only when flush is set to + Z_FINISH), Z_STREAM_ERROR if the stream state was inconsistent (for example + if next_in or next_out was Z_NULL), Z_BUF_ERROR if no progress is possible + (for example avail_in or avail_out was zero). Note that Z_BUF_ERROR is not + fatal, and deflate() can be called again with more input and more output + space to continue compressing. +*/ + + +ZEXTERN int ZEXPORT deflateEnd OF((z_streamp strm)); +/* + All dynamically allocated data structures for this stream are freed. + This function discards any unprocessed input and does not flush any pending + output. + + deflateEnd returns Z_OK if success, Z_STREAM_ERROR if the + stream state was inconsistent, Z_DATA_ERROR if the stream was freed + prematurely (some input or output was discarded). In the error case, msg + may be set but then points to a static string (which must not be + deallocated). +*/ + + +/* +ZEXTERN int ZEXPORT inflateInit OF((z_streamp strm)); + + Initializes the internal stream state for decompression. The fields + next_in, avail_in, zalloc, zfree and opaque must be initialized before by + the caller. If next_in is not Z_NULL and avail_in is large enough (the + exact value depends on the compression method), inflateInit determines the + compression method from the zlib header and allocates all data structures + accordingly; otherwise the allocation will be deferred to the first call of + inflate. If zalloc and zfree are set to Z_NULL, inflateInit updates them to + use default allocation functions. + + inflateInit returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_VERSION_ERROR if the zlib library version is incompatible with the + version assumed by the caller, or Z_STREAM_ERROR if the parameters are + invalid, such as a null pointer to the structure. msg is set to null if + there is no error message. inflateInit does not perform any decompression + apart from possibly reading the zlib header if present: actual decompression + will be done by inflate(). (So next_in and avail_in may be modified, but + next_out and avail_out are unused and unchanged.) The current implementation + of inflateInit() does not process any header information -- that is deferred + until inflate() is called. +*/ + + +ZEXTERN int ZEXPORT inflate OF((z_streamp strm, int flush)); +/* + inflate decompresses as much data as possible, and stops when the input + buffer becomes empty or the output buffer becomes full. It may introduce + some output latency (reading input without producing any output) except when + forced to flush. + + The detailed semantics are as follows. inflate performs one or both of the + following actions: + + - Decompress more input starting at next_in and update next_in and avail_in + accordingly. If not all input can be processed (because there is not + enough room in the output buffer), next_in is updated and processing will + resume at this point for the next call of inflate(). + + - Provide more output starting at next_out and update next_out and avail_out + accordingly. inflate() provides as much output as possible, until there is + no more input data or no more space in the output buffer (see below about + the flush parameter). + + Before the call of inflate(), the application should ensure that at least + one of the actions is possible, by providing more input and/or consuming more + output, and updating the next_* and avail_* values accordingly. The + application can consume the uncompressed output when it wants, for example + when the output buffer is full (avail_out == 0), or after each call of + inflate(). If inflate returns Z_OK and with zero avail_out, it must be + called again after making room in the output buffer because there might be + more output pending. + + The flush parameter of inflate() can be Z_NO_FLUSH, Z_SYNC_FLUSH, Z_FINISH, + Z_BLOCK, or Z_TREES. Z_SYNC_FLUSH requests that inflate() flush as much + output as possible to the output buffer. Z_BLOCK requests that inflate() + stop if and when it gets to the next deflate block boundary. When decoding + the zlib or gzip format, this will cause inflate() to return immediately + after the header and before the first block. When doing a raw inflate, + inflate() will go ahead and process the first block, and will return when it + gets to the end of that block, or when it runs out of data. + + The Z_BLOCK option assists in appending to or combining deflate streams. + Also to assist in this, on return inflate() will set strm->data_type to the + number of unused bits in the last byte taken from strm->next_in, plus 64 if + inflate() is currently decoding the last block in the deflate stream, plus + 128 if inflate() returned immediately after decoding an end-of-block code or + decoding the complete header up to just before the first byte of the deflate + stream. The end-of-block will not be indicated until all of the uncompressed + data from that block has been written to strm->next_out. The number of + unused bits may in general be greater than seven, except when bit 7 of + data_type is set, in which case the number of unused bits will be less than + eight. data_type is set as noted here every time inflate() returns for all + flush options, and so can be used to determine the amount of currently + consumed input in bits. + + The Z_TREES option behaves as Z_BLOCK does, but it also returns when the + end of each deflate block header is reached, before any actual data in that + block is decoded. This allows the caller to determine the length of the + deflate block header for later use in random access within a deflate block. + 256 is added to the value of strm->data_type when inflate() returns + immediately after reaching the end of the deflate block header. + + inflate() should normally be called until it returns Z_STREAM_END or an + error. However if all decompression is to be performed in a single step (a + single call of inflate), the parameter flush should be set to Z_FINISH. In + this case all pending input is processed and all pending output is flushed; + avail_out must be large enough to hold all of the uncompressed data for the + operation to complete. (The size of the uncompressed data may have been + saved by the compressor for this purpose.) The use of Z_FINISH is not + required to perform an inflation in one step. However it may be used to + inform inflate that a faster approach can be used for the single inflate() + call. Z_FINISH also informs inflate to not maintain a sliding window if the + stream completes, which reduces inflate's memory footprint. If the stream + does not complete, either because not all of the stream is provided or not + enough output space is provided, then a sliding window will be allocated and + inflate() can be called again to continue the operation as if Z_NO_FLUSH had + been used. + + In this implementation, inflate() always flushes as much output as + possible to the output buffer, and always uses the faster approach on the + first call. So the effects of the flush parameter in this implementation are + on the return value of inflate() as noted below, when inflate() returns early + when Z_BLOCK or Z_TREES is used, and when inflate() avoids the allocation of + memory for a sliding window when Z_FINISH is used. + + If a preset dictionary is needed after this call (see inflateSetDictionary + below), inflate sets strm->adler to the Adler-32 checksum of the dictionary + chosen by the compressor and returns Z_NEED_DICT; otherwise it sets + strm->adler to the Adler-32 checksum of all output produced so far (that is, + total_out bytes) and returns Z_OK, Z_STREAM_END or an error code as described + below. At the end of the stream, inflate() checks that its computed adler32 + checksum is equal to that saved by the compressor and returns Z_STREAM_END + only if the checksum is correct. + + inflate() can decompress and check either zlib-wrapped or gzip-wrapped + deflate data. The header type is detected automatically, if requested when + initializing with inflateInit2(). Any information contained in the gzip + header is not retained, so applications that need that information should + instead use raw inflate, see inflateInit2() below, or inflateBack() and + perform their own processing of the gzip header and trailer. When processing + gzip-wrapped deflate data, strm->adler32 is set to the CRC-32 of the output + producted so far. The CRC-32 is checked against the gzip trailer. + + inflate() returns Z_OK if some progress has been made (more input processed + or more output produced), Z_STREAM_END if the end of the compressed data has + been reached and all uncompressed output has been produced, Z_NEED_DICT if a + preset dictionary is needed at this point, Z_DATA_ERROR if the input data was + corrupted (input stream not conforming to the zlib format or incorrect check + value), Z_STREAM_ERROR if the stream structure was inconsistent (for example + next_in or next_out was Z_NULL), Z_MEM_ERROR if there was not enough memory, + Z_BUF_ERROR if no progress is possible or if there was not enough room in the + output buffer when Z_FINISH is used. Note that Z_BUF_ERROR is not fatal, and + inflate() can be called again with more input and more output space to + continue decompressing. If Z_DATA_ERROR is returned, the application may + then call inflateSync() to look for a good compression block if a partial + recovery of the data is desired. +*/ + + +ZEXTERN int ZEXPORT inflateEnd OF((z_streamp strm)); +/* + All dynamically allocated data structures for this stream are freed. + This function discards any unprocessed input and does not flush any pending + output. + + inflateEnd returns Z_OK if success, Z_STREAM_ERROR if the stream state + was inconsistent. In the error case, msg may be set but then points to a + static string (which must not be deallocated). +*/ + + + /* Advanced functions */ + +/* + The following functions are needed only in some special applications. +*/ + +/* +ZEXTERN int ZEXPORT deflateInit2 OF((z_streamp strm, + int level, + int method, + int windowBits, + int memLevel, + int strategy)); + + This is another version of deflateInit with more compression options. The + fields next_in, zalloc, zfree and opaque must be initialized before by the + caller. + + The method parameter is the compression method. It must be Z_DEFLATED in + this version of the library. + + The windowBits parameter is the base two logarithm of the window size + (the size of the history buffer). It should be in the range 8..15 for this + version of the library. Larger values of this parameter result in better + compression at the expense of memory usage. The default value is 15 if + deflateInit is used instead. + + windowBits can also be -8..-15 for raw deflate. In this case, -windowBits + determines the window size. deflate() will then generate raw deflate data + with no zlib header or trailer, and will not compute an adler32 check value. + + windowBits can also be greater than 15 for optional gzip encoding. Add + 16 to windowBits to write a simple gzip header and trailer around the + compressed data instead of a zlib wrapper. The gzip header will have no + file name, no extra data, no comment, no modification time (set to zero), no + header crc, and the operating system will be set to 255 (unknown). If a + gzip stream is being written, strm->adler is a crc32 instead of an adler32. + + The memLevel parameter specifies how much memory should be allocated + for the internal compression state. memLevel=1 uses minimum memory but is + slow and reduces compression ratio; memLevel=9 uses maximum memory for + optimal speed. The default value is 8. See zconf.h for total memory usage + as a function of windowBits and memLevel. + + The strategy parameter is used to tune the compression algorithm. Use the + value Z_DEFAULT_STRATEGY for normal data, Z_FILTERED for data produced by a + filter (or predictor), Z_HUFFMAN_ONLY to force Huffman encoding only (no + string match), or Z_RLE to limit match distances to one (run-length + encoding). Filtered data consists mostly of small values with a somewhat + random distribution. In this case, the compression algorithm is tuned to + compress them better. The effect of Z_FILTERED is to force more Huffman + coding and less string matching; it is somewhat intermediate between + Z_DEFAULT_STRATEGY and Z_HUFFMAN_ONLY. Z_RLE is designed to be almost as + fast as Z_HUFFMAN_ONLY, but give better compression for PNG image data. The + strategy parameter only affects the compression ratio but not the + correctness of the compressed output even if it is not set appropriately. + Z_FIXED prevents the use of dynamic Huffman codes, allowing for a simpler + decoder for special applications. + + deflateInit2 returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_STREAM_ERROR if any parameter is invalid (such as an invalid + method), or Z_VERSION_ERROR if the zlib library version (zlib_version) is + incompatible with the version assumed by the caller (ZLIB_VERSION). msg is + set to null if there is no error message. deflateInit2 does not perform any + compression: this will be done by deflate(). +*/ + +ZEXTERN int ZEXPORT deflateSetDictionary OF((z_streamp strm, + const Bytef *dictionary, + uInt dictLength)); +/* + Initializes the compression dictionary from the given byte sequence + without producing any compressed output. When using the zlib format, this + function must be called immediately after deflateInit, deflateInit2 or + deflateReset, and before any call of deflate. When doing raw deflate, this + function must be called either before any call of deflate, or immediately + after the completion of a deflate block, i.e. after all input has been + consumed and all output has been delivered when using any of the flush + options Z_BLOCK, Z_PARTIAL_FLUSH, Z_SYNC_FLUSH, or Z_FULL_FLUSH. The + compressor and decompressor must use exactly the same dictionary (see + inflateSetDictionary). + + The dictionary should consist of strings (byte sequences) that are likely + to be encountered later in the data to be compressed, with the most commonly + used strings preferably put towards the end of the dictionary. Using a + dictionary is most useful when the data to be compressed is short and can be + predicted with good accuracy; the data can then be compressed better than + with the default empty dictionary. + + Depending on the size of the compression data structures selected by + deflateInit or deflateInit2, a part of the dictionary may in effect be + discarded, for example if the dictionary is larger than the window size + provided in deflateInit or deflateInit2. Thus the strings most likely to be + useful should be put at the end of the dictionary, not at the front. In + addition, the current implementation of deflate will use at most the window + size minus 262 bytes of the provided dictionary. + + Upon return of this function, strm->adler is set to the adler32 value + of the dictionary; the decompressor may later use this value to determine + which dictionary has been used by the compressor. (The adler32 value + applies to the whole dictionary even if only a subset of the dictionary is + actually used by the compressor.) If a raw deflate was requested, then the + adler32 value is not computed and strm->adler is not set. + + deflateSetDictionary returns Z_OK if success, or Z_STREAM_ERROR if a + parameter is invalid (e.g. dictionary being Z_NULL) or the stream state is + inconsistent (for example if deflate has already been called for this stream + or if not at a block boundary for raw deflate). deflateSetDictionary does + not perform any compression: this will be done by deflate(). +*/ + +ZEXTERN int ZEXPORT deflateCopy OF((z_streamp dest, + z_streamp source)); +/* + Sets the destination stream as a complete copy of the source stream. + + This function can be useful when several compression strategies will be + tried, for example when there are several ways of pre-processing the input + data with a filter. The streams that will be discarded should then be freed + by calling deflateEnd. Note that deflateCopy duplicates the internal + compression state which can be quite large, so this strategy is slow and can + consume lots of memory. + + deflateCopy returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_STREAM_ERROR if the source stream state was inconsistent + (such as zalloc being Z_NULL). msg is left unchanged in both source and + destination. +*/ + +ZEXTERN int ZEXPORT deflateReset OF((z_streamp strm)); +/* + This function is equivalent to deflateEnd followed by deflateInit, + but does not free and reallocate all the internal compression state. The + stream will keep the same compression level and any other attributes that + may have been set by deflateInit2. + + deflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent (such as zalloc or state being Z_NULL). +*/ + +ZEXTERN int ZEXPORT deflateParams OF((z_streamp strm, + int level, + int strategy)); +/* + Dynamically update the compression level and compression strategy. The + interpretation of level and strategy is as in deflateInit2. This can be + used to switch between compression and straight copy of the input data, or + to switch to a different kind of input data requiring a different strategy. + If the compression level is changed, the input available so far is + compressed with the old level (and may be flushed); the new level will take + effect only at the next call of deflate(). + + Before the call of deflateParams, the stream state must be set as for + a call of deflate(), since the currently available input may have to be + compressed and flushed. In particular, strm->avail_out must be non-zero. + + deflateParams returns Z_OK if success, Z_STREAM_ERROR if the source + stream state was inconsistent or if a parameter was invalid, Z_BUF_ERROR if + strm->avail_out was zero. +*/ + +ZEXTERN int ZEXPORT deflateTune OF((z_streamp strm, + int good_length, + int max_lazy, + int nice_length, + int max_chain)); +/* + Fine tune deflate's internal compression parameters. This should only be + used by someone who understands the algorithm used by zlib's deflate for + searching for the best matching string, and even then only by the most + fanatic optimizer trying to squeeze out the last compressed bit for their + specific input data. Read the deflate.c source code for the meaning of the + max_lazy, good_length, nice_length, and max_chain parameters. + + deflateTune() can be called after deflateInit() or deflateInit2(), and + returns Z_OK on success, or Z_STREAM_ERROR for an invalid deflate stream. + */ + +ZEXTERN uLong ZEXPORT deflateBound OF((z_streamp strm, + uLong sourceLen)); +/* + deflateBound() returns an upper bound on the compressed size after + deflation of sourceLen bytes. It must be called after deflateInit() or + deflateInit2(), and after deflateSetHeader(), if used. This would be used + to allocate an output buffer for deflation in a single pass, and so would be + called before deflate(). If that first deflate() call is provided the + sourceLen input bytes, an output buffer allocated to the size returned by + deflateBound(), and the flush value Z_FINISH, then deflate() is guaranteed + to return Z_STREAM_END. Note that it is possible for the compressed size to + be larger than the value returned by deflateBound() if flush options other + than Z_FINISH or Z_NO_FLUSH are used. +*/ + +ZEXTERN int ZEXPORT deflatePending OF((z_streamp strm, + unsigned *pending, + int *bits)); +/* + deflatePending() returns the number of bytes and bits of output that have + been generated, but not yet provided in the available output. The bytes not + provided would be due to the available output space having being consumed. + The number of bits of output not provided are between 0 and 7, where they + await more bits to join them in order to fill out a full byte. If pending + or bits are Z_NULL, then those values are not set. + + deflatePending returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent. + */ + +ZEXTERN int ZEXPORT deflatePrime OF((z_streamp strm, + int bits, + int value)); +/* + deflatePrime() inserts bits in the deflate output stream. The intent + is that this function is used to start off the deflate output with the bits + leftover from a previous deflate stream when appending to it. As such, this + function can only be used for raw deflate, and must be used before the first + deflate() call after a deflateInit2() or deflateReset(). bits must be less + than or equal to 16, and that many of the least significant bits of value + will be inserted in the output. + + deflatePrime returns Z_OK if success, Z_BUF_ERROR if there was not enough + room in the internal buffer to insert the bits, or Z_STREAM_ERROR if the + source stream state was inconsistent. +*/ + +ZEXTERN int ZEXPORT deflateSetHeader OF((z_streamp strm, + gz_headerp head)); +/* + deflateSetHeader() provides gzip header information for when a gzip + stream is requested by deflateInit2(). deflateSetHeader() may be called + after deflateInit2() or deflateReset() and before the first call of + deflate(). The text, time, os, extra field, name, and comment information + in the provided gz_header structure are written to the gzip header (xflag is + ignored -- the extra flags are set according to the compression level). The + caller must assure that, if not Z_NULL, name and comment are terminated with + a zero byte, and that if extra is not Z_NULL, that extra_len bytes are + available there. If hcrc is true, a gzip header crc is included. Note that + the current versions of the command-line version of gzip (up through version + 1.3.x) do not support header crc's, and will report that it is a "multi-part + gzip file" and give up. + + If deflateSetHeader is not used, the default gzip header has text false, + the time set to zero, and os set to 255, with no extra, name, or comment + fields. The gzip header is returned to the default state by deflateReset(). + + deflateSetHeader returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent. +*/ + +/* +ZEXTERN int ZEXPORT inflateInit2 OF((z_streamp strm, + int windowBits)); + + This is another version of inflateInit with an extra parameter. The + fields next_in, avail_in, zalloc, zfree and opaque must be initialized + before by the caller. + + The windowBits parameter is the base two logarithm of the maximum window + size (the size of the history buffer). It should be in the range 8..15 for + this version of the library. The default value is 15 if inflateInit is used + instead. windowBits must be greater than or equal to the windowBits value + provided to deflateInit2() while compressing, or it must be equal to 15 if + deflateInit2() was not used. If a compressed stream with a larger window + size is given as input, inflate() will return with the error code + Z_DATA_ERROR instead of trying to allocate a larger window. + + windowBits can also be zero to request that inflate use the window size in + the zlib header of the compressed stream. + + windowBits can also be -8..-15 for raw inflate. In this case, -windowBits + determines the window size. inflate() will then process raw deflate data, + not looking for a zlib or gzip header, not generating a check value, and not + looking for any check values for comparison at the end of the stream. This + is for use with other formats that use the deflate compressed data format + such as zip. Those formats provide their own check values. If a custom + format is developed using the raw deflate format for compressed data, it is + recommended that a check value such as an adler32 or a crc32 be applied to + the uncompressed data as is done in the zlib, gzip, and zip formats. For + most applications, the zlib format should be used as is. Note that comments + above on the use in deflateInit2() applies to the magnitude of windowBits. + + windowBits can also be greater than 15 for optional gzip decoding. Add + 32 to windowBits to enable zlib and gzip decoding with automatic header + detection, or add 16 to decode only the gzip format (the zlib format will + return a Z_DATA_ERROR). If a gzip stream is being decoded, strm->adler is a + crc32 instead of an adler32. + + inflateInit2 returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_VERSION_ERROR if the zlib library version is incompatible with the + version assumed by the caller, or Z_STREAM_ERROR if the parameters are + invalid, such as a null pointer to the structure. msg is set to null if + there is no error message. inflateInit2 does not perform any decompression + apart from possibly reading the zlib header if present: actual decompression + will be done by inflate(). (So next_in and avail_in may be modified, but + next_out and avail_out are unused and unchanged.) The current implementation + of inflateInit2() does not process any header information -- that is + deferred until inflate() is called. +*/ + +ZEXTERN int ZEXPORT inflateSetDictionary OF((z_streamp strm, + const Bytef *dictionary, + uInt dictLength)); +/* + Initializes the decompression dictionary from the given uncompressed byte + sequence. This function must be called immediately after a call of inflate, + if that call returned Z_NEED_DICT. The dictionary chosen by the compressor + can be determined from the adler32 value returned by that call of inflate. + The compressor and decompressor must use exactly the same dictionary (see + deflateSetDictionary). For raw inflate, this function can be called at any + time to set the dictionary. If the provided dictionary is smaller than the + window and there is already data in the window, then the provided dictionary + will amend what's there. The application must insure that the dictionary + that was used for compression is provided. + + inflateSetDictionary returns Z_OK if success, Z_STREAM_ERROR if a + parameter is invalid (e.g. dictionary being Z_NULL) or the stream state is + inconsistent, Z_DATA_ERROR if the given dictionary doesn't match the + expected one (incorrect adler32 value). inflateSetDictionary does not + perform any decompression: this will be done by subsequent calls of + inflate(). +*/ + +ZEXTERN int ZEXPORT inflateGetDictionary OF((z_streamp strm, + Bytef *dictionary, + uInt *dictLength)); +/* + Returns the sliding dictionary being maintained by inflate. dictLength is + set to the number of bytes in the dictionary, and that many bytes are copied + to dictionary. dictionary must have enough space, where 32768 bytes is + always enough. If inflateGetDictionary() is called with dictionary equal to + Z_NULL, then only the dictionary length is returned, and nothing is copied. + Similary, if dictLength is Z_NULL, then it is not set. + + inflateGetDictionary returns Z_OK on success, or Z_STREAM_ERROR if the + stream state is inconsistent. +*/ + +ZEXTERN int ZEXPORT inflateSync OF((z_streamp strm)); +/* + Skips invalid compressed data until a possible full flush point (see above + for the description of deflate with Z_FULL_FLUSH) can be found, or until all + available input is skipped. No output is provided. + + inflateSync searches for a 00 00 FF FF pattern in the compressed data. + All full flush points have this pattern, but not all occurrences of this + pattern are full flush points. + + inflateSync returns Z_OK if a possible full flush point has been found, + Z_BUF_ERROR if no more input was provided, Z_DATA_ERROR if no flush point + has been found, or Z_STREAM_ERROR if the stream structure was inconsistent. + In the success case, the application may save the current current value of + total_in which indicates where valid compressed data was found. In the + error case, the application may repeatedly call inflateSync, providing more + input each time, until success or end of the input data. +*/ + +ZEXTERN int ZEXPORT inflateCopy OF((z_streamp dest, + z_streamp source)); +/* + Sets the destination stream as a complete copy of the source stream. + + This function can be useful when randomly accessing a large stream. The + first pass through the stream can periodically record the inflate state, + allowing restarting inflate at those points when randomly accessing the + stream. + + inflateCopy returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_STREAM_ERROR if the source stream state was inconsistent + (such as zalloc being Z_NULL). msg is left unchanged in both source and + destination. +*/ + +ZEXTERN int ZEXPORT inflateReset OF((z_streamp strm)); +/* + This function is equivalent to inflateEnd followed by inflateInit, + but does not free and reallocate all the internal decompression state. The + stream will keep attributes that may have been set by inflateInit2. + + inflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent (such as zalloc or state being Z_NULL). +*/ + +ZEXTERN int ZEXPORT inflateReset2 OF((z_streamp strm, + int windowBits)); +/* + This function is the same as inflateReset, but it also permits changing + the wrap and window size requests. The windowBits parameter is interpreted + the same as it is for inflateInit2. + + inflateReset2 returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent (such as zalloc or state being Z_NULL), or if + the windowBits parameter is invalid. +*/ + +ZEXTERN int ZEXPORT inflatePrime OF((z_streamp strm, + int bits, + int value)); +/* + This function inserts bits in the inflate input stream. The intent is + that this function is used to start inflating at a bit position in the + middle of a byte. The provided bits will be used before any bytes are used + from next_in. This function should only be used with raw inflate, and + should be used before the first inflate() call after inflateInit2() or + inflateReset(). bits must be less than or equal to 16, and that many of the + least significant bits of value will be inserted in the input. + + If bits is negative, then the input stream bit buffer is emptied. Then + inflatePrime() can be called again to put bits in the buffer. This is used + to clear out bits leftover after feeding inflate a block description prior + to feeding inflate codes. + + inflatePrime returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent. +*/ + +ZEXTERN long ZEXPORT inflateMark OF((z_streamp strm)); +/* + This function returns two values, one in the lower 16 bits of the return + value, and the other in the remaining upper bits, obtained by shifting the + return value down 16 bits. If the upper value is -1 and the lower value is + zero, then inflate() is currently decoding information outside of a block. + If the upper value is -1 and the lower value is non-zero, then inflate is in + the middle of a stored block, with the lower value equaling the number of + bytes from the input remaining to copy. If the upper value is not -1, then + it is the number of bits back from the current bit position in the input of + the code (literal or length/distance pair) currently being processed. In + that case the lower value is the number of bytes already emitted for that + code. + + A code is being processed if inflate is waiting for more input to complete + decoding of the code, or if it has completed decoding but is waiting for + more output space to write the literal or match data. + + inflateMark() is used to mark locations in the input data for random + access, which may be at bit positions, and to note those cases where the + output of a code may span boundaries of random access blocks. The current + location in the input stream can be determined from avail_in and data_type + as noted in the description for the Z_BLOCK flush parameter for inflate. + + inflateMark returns the value noted above or -1 << 16 if the provided + source stream state was inconsistent. +*/ + +ZEXTERN int ZEXPORT inflateGetHeader OF((z_streamp strm, + gz_headerp head)); +/* + inflateGetHeader() requests that gzip header information be stored in the + provided gz_header structure. inflateGetHeader() may be called after + inflateInit2() or inflateReset(), and before the first call of inflate(). + As inflate() processes the gzip stream, head->done is zero until the header + is completed, at which time head->done is set to one. If a zlib stream is + being decoded, then head->done is set to -1 to indicate that there will be + no gzip header information forthcoming. Note that Z_BLOCK or Z_TREES can be + used to force inflate() to return immediately after header processing is + complete and before any actual data is decompressed. + + The text, time, xflags, and os fields are filled in with the gzip header + contents. hcrc is set to true if there is a header CRC. (The header CRC + was valid if done is set to one.) If extra is not Z_NULL, then extra_max + contains the maximum number of bytes to write to extra. Once done is true, + extra_len contains the actual extra field length, and extra contains the + extra field, or that field truncated if extra_max is less than extra_len. + If name is not Z_NULL, then up to name_max characters are written there, + terminated with a zero unless the length is greater than name_max. If + comment is not Z_NULL, then up to comm_max characters are written there, + terminated with a zero unless the length is greater than comm_max. When any + of extra, name, or comment are not Z_NULL and the respective field is not + present in the header, then that field is set to Z_NULL to signal its + absence. This allows the use of deflateSetHeader() with the returned + structure to duplicate the header. However if those fields are set to + allocated memory, then the application will need to save those pointers + elsewhere so that they can be eventually freed. + + If inflateGetHeader is not used, then the header information is simply + discarded. The header is always checked for validity, including the header + CRC if present. inflateReset() will reset the process to discard the header + information. The application would need to call inflateGetHeader() again to + retrieve the header from the next gzip stream. + + inflateGetHeader returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent. +*/ + +/* +ZEXTERN int ZEXPORT inflateBackInit OF((z_streamp strm, int windowBits, + unsigned char FAR *window)); + + Initialize the internal stream state for decompression using inflateBack() + calls. The fields zalloc, zfree and opaque in strm must be initialized + before the call. If zalloc and zfree are Z_NULL, then the default library- + derived memory allocation routines are used. windowBits is the base two + logarithm of the window size, in the range 8..15. window is a caller + supplied buffer of that size. Except for special applications where it is + assured that deflate was used with small window sizes, windowBits must be 15 + and a 32K byte window must be supplied to be able to decompress general + deflate streams. + + See inflateBack() for the usage of these routines. + + inflateBackInit will return Z_OK on success, Z_STREAM_ERROR if any of + the parameters are invalid, Z_MEM_ERROR if the internal state could not be + allocated, or Z_VERSION_ERROR if the version of the library does not match + the version of the header file. +*/ + +typedef unsigned (*in_func) OF((void FAR *, + z_const unsigned char FAR * FAR *)); +typedef int (*out_func) OF((void FAR *, unsigned char FAR *, unsigned)); + +ZEXTERN int ZEXPORT inflateBack OF((z_streamp strm, + in_func in, void FAR *in_desc, + out_func out, void FAR *out_desc)); +/* + inflateBack() does a raw inflate with a single call using a call-back + interface for input and output. This is potentially more efficient than + inflate() for file i/o applications, in that it avoids copying between the + output and the sliding window by simply making the window itself the output + buffer. inflate() can be faster on modern CPUs when used with large + buffers. inflateBack() trusts the application to not change the output + buffer passed by the output function, at least until inflateBack() returns. + + inflateBackInit() must be called first to allocate the internal state + and to initialize the state with the user-provided window buffer. + inflateBack() may then be used multiple times to inflate a complete, raw + deflate stream with each call. inflateBackEnd() is then called to free the + allocated state. + + A raw deflate stream is one with no zlib or gzip header or trailer. + This routine would normally be used in a utility that reads zip or gzip + files and writes out uncompressed files. The utility would decode the + header and process the trailer on its own, hence this routine expects only + the raw deflate stream to decompress. This is different from the normal + behavior of inflate(), which expects either a zlib or gzip header and + trailer around the deflate stream. + + inflateBack() uses two subroutines supplied by the caller that are then + called by inflateBack() for input and output. inflateBack() calls those + routines until it reads a complete deflate stream and writes out all of the + uncompressed data, or until it encounters an error. The function's + parameters and return types are defined above in the in_func and out_func + typedefs. inflateBack() will call in(in_desc, &buf) which should return the + number of bytes of provided input, and a pointer to that input in buf. If + there is no input available, in() must return zero--buf is ignored in that + case--and inflateBack() will return a buffer error. inflateBack() will call + out(out_desc, buf, len) to write the uncompressed data buf[0..len-1]. out() + should return zero on success, or non-zero on failure. If out() returns + non-zero, inflateBack() will return with an error. Neither in() nor out() + are permitted to change the contents of the window provided to + inflateBackInit(), which is also the buffer that out() uses to write from. + The length written by out() will be at most the window size. Any non-zero + amount of input may be provided by in(). + + For convenience, inflateBack() can be provided input on the first call by + setting strm->next_in and strm->avail_in. If that input is exhausted, then + in() will be called. Therefore strm->next_in must be initialized before + calling inflateBack(). If strm->next_in is Z_NULL, then in() will be called + immediately for input. If strm->next_in is not Z_NULL, then strm->avail_in + must also be initialized, and then if strm->avail_in is not zero, input will + initially be taken from strm->next_in[0 .. strm->avail_in - 1]. + + The in_desc and out_desc parameters of inflateBack() is passed as the + first parameter of in() and out() respectively when they are called. These + descriptors can be optionally used to pass any information that the caller- + supplied in() and out() functions need to do their job. + + On return, inflateBack() will set strm->next_in and strm->avail_in to + pass back any unused input that was provided by the last in() call. The + return values of inflateBack() can be Z_STREAM_END on success, Z_BUF_ERROR + if in() or out() returned an error, Z_DATA_ERROR if there was a format error + in the deflate stream (in which case strm->msg is set to indicate the nature + of the error), or Z_STREAM_ERROR if the stream was not properly initialized. + In the case of Z_BUF_ERROR, an input or output error can be distinguished + using strm->next_in which will be Z_NULL only if in() returned an error. If + strm->next_in is not Z_NULL, then the Z_BUF_ERROR was due to out() returning + non-zero. (in() will always be called before out(), so strm->next_in is + assured to be defined if out() returns non-zero.) Note that inflateBack() + cannot return Z_OK. +*/ + +ZEXTERN int ZEXPORT inflateBackEnd OF((z_streamp strm)); +/* + All memory allocated by inflateBackInit() is freed. + + inflateBackEnd() returns Z_OK on success, or Z_STREAM_ERROR if the stream + state was inconsistent. +*/ + +ZEXTERN uLong ZEXPORT zlibCompileFlags OF((void)); +/* Return flags indicating compile-time options. + + Type sizes, two bits each, 00 = 16 bits, 01 = 32, 10 = 64, 11 = other: + 1.0: size of uInt + 3.2: size of uLong + 5.4: size of voidpf (pointer) + 7.6: size of z_off_t + + Compiler, assembler, and debug options: + 8: DEBUG + 9: ASMV or ASMINF -- use ASM code + 10: ZLIB_WINAPI -- exported functions use the WINAPI calling convention + 11: 0 (reserved) + + One-time table building (smaller code, but not thread-safe if true): + 12: BUILDFIXED -- build static block decoding tables when needed + 13: DYNAMIC_CRC_TABLE -- build CRC calculation tables when needed + 14,15: 0 (reserved) + + Library content (indicates missing functionality): + 16: NO_GZCOMPRESS -- gz* functions cannot compress (to avoid linking + deflate code when not needed) + 17: NO_GZIP -- deflate can't write gzip streams, and inflate can't detect + and decode gzip streams (to avoid linking crc code) + 18-19: 0 (reserved) + + Operation variations (changes in library functionality): + 20: PKZIP_BUG_WORKAROUND -- slightly more permissive inflate + 21: FASTEST -- deflate algorithm with only one, lowest compression level + 22,23: 0 (reserved) + + The sprintf variant used by gzprintf (zero is best): + 24: 0 = vs*, 1 = s* -- 1 means limited to 20 arguments after the format + 25: 0 = *nprintf, 1 = *printf -- 1 means gzprintf() not secure! + 26: 0 = returns value, 1 = void -- 1 means inferred string length returned + + Remainder: + 27-31: 0 (reserved) + */ + +#ifndef Z_SOLO + + /* utility functions */ + +/* + The following utility functions are implemented on top of the basic + stream-oriented functions. To simplify the interface, some default options + are assumed (compression level and memory usage, standard memory allocation + functions). The source code of these utility functions can be modified if + you need special options. +*/ + +ZEXTERN int ZEXPORT compress OF((Bytef *dest, uLongf *destLen, + const Bytef *source, uLong sourceLen)); +/* + Compresses the source buffer into the destination buffer. sourceLen is + the byte length of the source buffer. Upon entry, destLen is the total size + of the destination buffer, which must be at least the value returned by + compressBound(sourceLen). Upon exit, destLen is the actual size of the + compressed buffer. + + compress returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_BUF_ERROR if there was not enough room in the output + buffer. +*/ + +ZEXTERN int ZEXPORT compress2 OF((Bytef *dest, uLongf *destLen, + const Bytef *source, uLong sourceLen, + int level)); +/* + Compresses the source buffer into the destination buffer. The level + parameter has the same meaning as in deflateInit. sourceLen is the byte + length of the source buffer. Upon entry, destLen is the total size of the + destination buffer, which must be at least the value returned by + compressBound(sourceLen). Upon exit, destLen is the actual size of the + compressed buffer. + + compress2 returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_BUF_ERROR if there was not enough room in the output buffer, + Z_STREAM_ERROR if the level parameter is invalid. +*/ + +ZEXTERN uLong ZEXPORT compressBound OF((uLong sourceLen)); +/* + compressBound() returns an upper bound on the compressed size after + compress() or compress2() on sourceLen bytes. It would be used before a + compress() or compress2() call to allocate the destination buffer. +*/ + +ZEXTERN int ZEXPORT uncompress OF((Bytef *dest, uLongf *destLen, + const Bytef *source, uLong sourceLen)); +/* + Decompresses the source buffer into the destination buffer. sourceLen is + the byte length of the source buffer. Upon entry, destLen is the total size + of the destination buffer, which must be large enough to hold the entire + uncompressed data. (The size of the uncompressed data must have been saved + previously by the compressor and transmitted to the decompressor by some + mechanism outside the scope of this compression library.) Upon exit, destLen + is the actual size of the uncompressed buffer. + + uncompress returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_BUF_ERROR if there was not enough room in the output + buffer, or Z_DATA_ERROR if the input data was corrupted or incomplete. In + the case where there is not enough room, uncompress() will fill the output + buffer with the uncompressed data up to that point. +*/ + + /* gzip file access functions */ + +/* + This library supports reading and writing files in gzip (.gz) format with + an interface similar to that of stdio, using the functions that start with + "gz". The gzip format is different from the zlib format. gzip is a gzip + wrapper, documented in RFC 1952, wrapped around a deflate stream. +*/ + +typedef struct gzFile_s *gzFile; /* semi-opaque gzip file descriptor */ + +/* +ZEXTERN gzFile ZEXPORT gzopen OF((const char *path, const char *mode)); + + Opens a gzip (.gz) file for reading or writing. The mode parameter is as + in fopen ("rb" or "wb") but can also include a compression level ("wb9") or + a strategy: 'f' for filtered data as in "wb6f", 'h' for Huffman-only + compression as in "wb1h", 'R' for run-length encoding as in "wb1R", or 'F' + for fixed code compression as in "wb9F". (See the description of + deflateInit2 for more information about the strategy parameter.) 'T' will + request transparent writing or appending with no compression and not using + the gzip format. + + "a" can be used instead of "w" to request that the gzip stream that will + be written be appended to the file. "+" will result in an error, since + reading and writing to the same gzip file is not supported. The addition of + "x" when writing will create the file exclusively, which fails if the file + already exists. On systems that support it, the addition of "e" when + reading or writing will set the flag to close the file on an execve() call. + + These functions, as well as gzip, will read and decode a sequence of gzip + streams in a file. The append function of gzopen() can be used to create + such a file. (Also see gzflush() for another way to do this.) When + appending, gzopen does not test whether the file begins with a gzip stream, + nor does it look for the end of the gzip streams to begin appending. gzopen + will simply append a gzip stream to the existing file. + + gzopen can be used to read a file which is not in gzip format; in this + case gzread will directly read from the file without decompression. When + reading, this will be detected automatically by looking for the magic two- + byte gzip header. + + gzopen returns NULL if the file could not be opened, if there was + insufficient memory to allocate the gzFile state, or if an invalid mode was + specified (an 'r', 'w', or 'a' was not provided, or '+' was provided). + errno can be checked to determine if the reason gzopen failed was that the + file could not be opened. +*/ + +ZEXTERN gzFile ZEXPORT gzdopen OF((int fd, const char *mode)); +/* + gzdopen associates a gzFile with the file descriptor fd. File descriptors + are obtained from calls like open, dup, creat, pipe or fileno (if the file + has been previously opened with fopen). The mode parameter is as in gzopen. + + The next call of gzclose on the returned gzFile will also close the file + descriptor fd, just like fclose(fdopen(fd, mode)) closes the file descriptor + fd. If you want to keep fd open, use fd = dup(fd_keep); gz = gzdopen(fd, + mode);. The duplicated descriptor should be saved to avoid a leak, since + gzdopen does not close fd if it fails. If you are using fileno() to get the + file descriptor from a FILE *, then you will have to use dup() to avoid + double-close()ing the file descriptor. Both gzclose() and fclose() will + close the associated file descriptor, so they need to have different file + descriptors. + + gzdopen returns NULL if there was insufficient memory to allocate the + gzFile state, if an invalid mode was specified (an 'r', 'w', or 'a' was not + provided, or '+' was provided), or if fd is -1. The file descriptor is not + used until the next gz* read, write, seek, or close operation, so gzdopen + will not detect if fd is invalid (unless fd is -1). +*/ + +ZEXTERN int ZEXPORT gzbuffer OF((gzFile file, unsigned size)); +/* + Set the internal buffer size used by this library's functions. The + default buffer size is 8192 bytes. This function must be called after + gzopen() or gzdopen(), and before any other calls that read or write the + file. The buffer memory allocation is always deferred to the first read or + write. Two buffers are allocated, either both of the specified size when + writing, or one of the specified size and the other twice that size when + reading. A larger buffer size of, for example, 64K or 128K bytes will + noticeably increase the speed of decompression (reading). + + The new buffer size also affects the maximum length for gzprintf(). + + gzbuffer() returns 0 on success, or -1 on failure, such as being called + too late. +*/ + +ZEXTERN int ZEXPORT gzsetparams OF((gzFile file, int level, int strategy)); +/* + Dynamically update the compression level or strategy. See the description + of deflateInit2 for the meaning of these parameters. + + gzsetparams returns Z_OK if success, or Z_STREAM_ERROR if the file was not + opened for writing. +*/ + +ZEXTERN int ZEXPORT gzread OF((gzFile file, voidp buf, unsigned len)); +/* + Reads the given number of uncompressed bytes from the compressed file. If + the input file is not in gzip format, gzread copies the given number of + bytes into the buffer directly from the file. + + After reaching the end of a gzip stream in the input, gzread will continue + to read, looking for another gzip stream. Any number of gzip streams may be + concatenated in the input file, and will all be decompressed by gzread(). + If something other than a gzip stream is encountered after a gzip stream, + that remaining trailing garbage is ignored (and no error is returned). + + gzread can be used to read a gzip file that is being concurrently written. + Upon reaching the end of the input, gzread will return with the available + data. If the error code returned by gzerror is Z_OK or Z_BUF_ERROR, then + gzclearerr can be used to clear the end of file indicator in order to permit + gzread to be tried again. Z_OK indicates that a gzip stream was completed + on the last gzread. Z_BUF_ERROR indicates that the input file ended in the + middle of a gzip stream. Note that gzread does not return -1 in the event + of an incomplete gzip stream. This error is deferred until gzclose(), which + will return Z_BUF_ERROR if the last gzread ended in the middle of a gzip + stream. Alternatively, gzerror can be used before gzclose to detect this + case. + + gzread returns the number of uncompressed bytes actually read, less than + len for end of file, or -1 for error. +*/ + +ZEXTERN int ZEXPORT gzwrite OF((gzFile file, + voidpc buf, unsigned len)); +/* + Writes the given number of uncompressed bytes into the compressed file. + gzwrite returns the number of uncompressed bytes written or 0 in case of + error. +*/ + +ZEXTERN int ZEXPORTVA gzprintf Z_ARG((gzFile file, const char *format, ...)); +/* + Converts, formats, and writes the arguments to the compressed file under + control of the format string, as in fprintf. gzprintf returns the number of + uncompressed bytes actually written, or 0 in case of error. The number of + uncompressed bytes written is limited to 8191, or one less than the buffer + size given to gzbuffer(). The caller should assure that this limit is not + exceeded. If it is exceeded, then gzprintf() will return an error (0) with + nothing written. In this case, there may also be a buffer overflow with + unpredictable consequences, which is possible only if zlib was compiled with + the insecure functions sprintf() or vsprintf() because the secure snprintf() + or vsnprintf() functions were not available. This can be determined using + zlibCompileFlags(). +*/ + +ZEXTERN int ZEXPORT gzputs OF((gzFile file, const char *s)); +/* + Writes the given null-terminated string to the compressed file, excluding + the terminating null character. + + gzputs returns the number of characters written, or -1 in case of error. +*/ + +ZEXTERN char * ZEXPORT gzgets OF((gzFile file, char *buf, int len)); +/* + Reads bytes from the compressed file until len-1 characters are read, or a + newline character is read and transferred to buf, or an end-of-file + condition is encountered. If any characters are read or if len == 1, the + string is terminated with a null character. If no characters are read due + to an end-of-file or len < 1, then the buffer is left untouched. + + gzgets returns buf which is a null-terminated string, or it returns NULL + for end-of-file or in case of error. If there was an error, the contents at + buf are indeterminate. +*/ + +ZEXTERN int ZEXPORT gzputc OF((gzFile file, int c)); +/* + Writes c, converted to an unsigned char, into the compressed file. gzputc + returns the value that was written, or -1 in case of error. +*/ + +ZEXTERN int ZEXPORT gzgetc OF((gzFile file)); +/* + Reads one byte from the compressed file. gzgetc returns this byte or -1 + in case of end of file or error. This is implemented as a macro for speed. + As such, it does not do all of the checking the other functions do. I.e. + it does not check to see if file is NULL, nor whether the structure file + points to has been clobbered or not. +*/ + +ZEXTERN int ZEXPORT gzungetc OF((int c, gzFile file)); +/* + Push one character back onto the stream to be read as the first character + on the next read. At least one character of push-back is allowed. + gzungetc() returns the character pushed, or -1 on failure. gzungetc() will + fail if c is -1, and may fail if a character has been pushed but not read + yet. If gzungetc is used immediately after gzopen or gzdopen, at least the + output buffer size of pushed characters is allowed. (See gzbuffer above.) + The pushed character will be discarded if the stream is repositioned with + gzseek() or gzrewind(). +*/ + +ZEXTERN int ZEXPORT gzflush OF((gzFile file, int flush)); +/* + Flushes all pending output into the compressed file. The parameter flush + is as in the deflate() function. The return value is the zlib error number + (see function gzerror below). gzflush is only permitted when writing. + + If the flush parameter is Z_FINISH, the remaining data is written and the + gzip stream is completed in the output. If gzwrite() is called again, a new + gzip stream will be started in the output. gzread() is able to read such + concatented gzip streams. + + gzflush should be called only when strictly necessary because it will + degrade compression if called too often. +*/ + +/* +ZEXTERN z_off_t ZEXPORT gzseek OF((gzFile file, + z_off_t offset, int whence)); + + Sets the starting position for the next gzread or gzwrite on the given + compressed file. The offset represents a number of bytes in the + uncompressed data stream. The whence parameter is defined as in lseek(2); + the value SEEK_END is not supported. + + If the file is opened for reading, this function is emulated but can be + extremely slow. If the file is opened for writing, only forward seeks are + supported; gzseek then compresses a sequence of zeroes up to the new + starting position. + + gzseek returns the resulting offset location as measured in bytes from + the beginning of the uncompressed stream, or -1 in case of error, in + particular if the file is opened for writing and the new starting position + would be before the current position. +*/ + +ZEXTERN int ZEXPORT gzrewind OF((gzFile file)); +/* + Rewinds the given file. This function is supported only for reading. + + gzrewind(file) is equivalent to (int)gzseek(file, 0L, SEEK_SET) +*/ + +/* +ZEXTERN z_off_t ZEXPORT gztell OF((gzFile file)); + + Returns the starting position for the next gzread or gzwrite on the given + compressed file. This position represents a number of bytes in the + uncompressed data stream, and is zero when starting, even if appending or + reading a gzip stream from the middle of a file using gzdopen(). + + gztell(file) is equivalent to gzseek(file, 0L, SEEK_CUR) +*/ + +/* +ZEXTERN z_off_t ZEXPORT gzoffset OF((gzFile file)); + + Returns the current offset in the file being read or written. This offset + includes the count of bytes that precede the gzip stream, for example when + appending or when using gzdopen() for reading. When reading, the offset + does not include as yet unused buffered input. This information can be used + for a progress indicator. On error, gzoffset() returns -1. +*/ + +ZEXTERN int ZEXPORT gzeof OF((gzFile file)); +/* + Returns true (1) if the end-of-file indicator has been set while reading, + false (0) otherwise. Note that the end-of-file indicator is set only if the + read tried to go past the end of the input, but came up short. Therefore, + just like feof(), gzeof() may return false even if there is no more data to + read, in the event that the last read request was for the exact number of + bytes remaining in the input file. This will happen if the input file size + is an exact multiple of the buffer size. + + If gzeof() returns true, then the read functions will return no more data, + unless the end-of-file indicator is reset by gzclearerr() and the input file + has grown since the previous end of file was detected. +*/ + +ZEXTERN int ZEXPORT gzdirect OF((gzFile file)); +/* + Returns true (1) if file is being copied directly while reading, or false + (0) if file is a gzip stream being decompressed. + + If the input file is empty, gzdirect() will return true, since the input + does not contain a gzip stream. + + If gzdirect() is used immediately after gzopen() or gzdopen() it will + cause buffers to be allocated to allow reading the file to determine if it + is a gzip file. Therefore if gzbuffer() is used, it should be called before + gzdirect(). + + When writing, gzdirect() returns true (1) if transparent writing was + requested ("wT" for the gzopen() mode), or false (0) otherwise. (Note: + gzdirect() is not needed when writing. Transparent writing must be + explicitly requested, so the application already knows the answer. When + linking statically, using gzdirect() will include all of the zlib code for + gzip file reading and decompression, which may not be desired.) +*/ + +ZEXTERN int ZEXPORT gzclose OF((gzFile file)); +/* + Flushes all pending output if necessary, closes the compressed file and + deallocates the (de)compression state. Note that once file is closed, you + cannot call gzerror with file, since its structures have been deallocated. + gzclose must not be called more than once on the same file, just as free + must not be called more than once on the same allocation. + + gzclose will return Z_STREAM_ERROR if file is not valid, Z_ERRNO on a + file operation error, Z_MEM_ERROR if out of memory, Z_BUF_ERROR if the + last read ended in the middle of a gzip stream, or Z_OK on success. +*/ + +ZEXTERN int ZEXPORT gzclose_r OF((gzFile file)); +ZEXTERN int ZEXPORT gzclose_w OF((gzFile file)); +/* + Same as gzclose(), but gzclose_r() is only for use when reading, and + gzclose_w() is only for use when writing or appending. The advantage to + using these instead of gzclose() is that they avoid linking in zlib + compression or decompression code that is not used when only reading or only + writing respectively. If gzclose() is used, then both compression and + decompression code will be included the application when linking to a static + zlib library. +*/ + +ZEXTERN const char * ZEXPORT gzerror OF((gzFile file, int *errnum)); +/* + Returns the error message for the last error which occurred on the given + compressed file. errnum is set to zlib error number. If an error occurred + in the file system and not in the compression library, errnum is set to + Z_ERRNO and the application may consult errno to get the exact error code. + + The application must not modify the returned string. Future calls to + this function may invalidate the previously returned string. If file is + closed, then the string previously returned by gzerror will no longer be + available. + + gzerror() should be used to distinguish errors from end-of-file for those + functions above that do not distinguish those cases in their return values. +*/ + +ZEXTERN void ZEXPORT gzclearerr OF((gzFile file)); +/* + Clears the error and end-of-file flags for file. This is analogous to the + clearerr() function in stdio. This is useful for continuing to read a gzip + file that is being written concurrently. +*/ + +#endif /* !Z_SOLO */ + + /* checksum functions */ + +/* + These functions are not related to compression but are exported + anyway because they might be useful in applications using the compression + library. +*/ + +ZEXTERN uLong ZEXPORT adler32 OF((uLong adler, const Bytef *buf, uInt len)); +/* + Update a running Adler-32 checksum with the bytes buf[0..len-1] and + return the updated checksum. If buf is Z_NULL, this function returns the + required initial value for the checksum. + + An Adler-32 checksum is almost as reliable as a CRC32 but can be computed + much faster. + + Usage example: + + uLong adler = adler32(0L, Z_NULL, 0); + + while (read_buffer(buffer, length) != EOF) { + adler = adler32(adler, buffer, length); + } + if (adler != original_adler) error(); +*/ + +/* +ZEXTERN uLong ZEXPORT adler32_combine OF((uLong adler1, uLong adler2, + z_off_t len2)); + + Combine two Adler-32 checksums into one. For two sequences of bytes, seq1 + and seq2 with lengths len1 and len2, Adler-32 checksums were calculated for + each, adler1 and adler2. adler32_combine() returns the Adler-32 checksum of + seq1 and seq2 concatenated, requiring only adler1, adler2, and len2. Note + that the z_off_t type (like off_t) is a signed integer. If len2 is + negative, the result has no meaning or utility. +*/ + +ZEXTERN uLong ZEXPORT crc32 OF((uLong crc, const Bytef *buf, uInt len)); +/* + Update a running CRC-32 with the bytes buf[0..len-1] and return the + updated CRC-32. If buf is Z_NULL, this function returns the required + initial value for the crc. Pre- and post-conditioning (one's complement) is + performed within this function so it shouldn't be done by the application. + + Usage example: + + uLong crc = crc32(0L, Z_NULL, 0); + + while (read_buffer(buffer, length) != EOF) { + crc = crc32(crc, buffer, length); + } + if (crc != original_crc) error(); +*/ + +/* +ZEXTERN uLong ZEXPORT crc32_combine OF((uLong crc1, uLong crc2, z_off_t len2)); + + Combine two CRC-32 check values into one. For two sequences of bytes, + seq1 and seq2 with lengths len1 and len2, CRC-32 check values were + calculated for each, crc1 and crc2. crc32_combine() returns the CRC-32 + check value of seq1 and seq2 concatenated, requiring only crc1, crc2, and + len2. +*/ + + + /* various hacks, don't look :) */ + +/* deflateInit and inflateInit are macros to allow checking the zlib version + * and the compiler's view of z_stream: + */ +ZEXTERN int ZEXPORT deflateInit_ OF((z_streamp strm, int level, + const char *version, int stream_size)); +ZEXTERN int ZEXPORT inflateInit_ OF((z_streamp strm, + const char *version, int stream_size)); +ZEXTERN int ZEXPORT deflateInit2_ OF((z_streamp strm, int level, int method, + int windowBits, int memLevel, + int strategy, const char *version, + int stream_size)); +ZEXTERN int ZEXPORT inflateInit2_ OF((z_streamp strm, int windowBits, + const char *version, int stream_size)); +ZEXTERN int ZEXPORT inflateBackInit_ OF((z_streamp strm, int windowBits, + unsigned char FAR *window, + const char *version, + int stream_size)); +#define deflateInit(strm, level) \ + deflateInit_((strm), (level), ZLIB_VERSION, (int)sizeof(z_stream)) +#define inflateInit(strm) \ + inflateInit_((strm), ZLIB_VERSION, (int)sizeof(z_stream)) +#define deflateInit2(strm, level, method, windowBits, memLevel, strategy) \ + deflateInit2_((strm),(level),(method),(windowBits),(memLevel),\ + (strategy), ZLIB_VERSION, (int)sizeof(z_stream)) +#define inflateInit2(strm, windowBits) \ + inflateInit2_((strm), (windowBits), ZLIB_VERSION, \ + (int)sizeof(z_stream)) +#define inflateBackInit(strm, windowBits, window) \ + inflateBackInit_((strm), (windowBits), (window), \ + ZLIB_VERSION, (int)sizeof(z_stream)) + +#ifndef Z_SOLO + +/* gzgetc() macro and its supporting function and exposed data structure. Note + * that the real internal state is much larger than the exposed structure. + * This abbreviated structure exposes just enough for the gzgetc() macro. The + * user should not mess with these exposed elements, since their names or + * behavior could change in the future, perhaps even capriciously. They can + * only be used by the gzgetc() macro. You have been warned. + */ +struct gzFile_s { + unsigned have; + unsigned char *next; + z_off64_t pos; +}; +ZEXTERN int ZEXPORT gzgetc_ OF((gzFile file)); /* backward compatibility */ +#ifdef Z_PREFIX_SET +# undef z_gzgetc +# define z_gzgetc(g) \ + ((g)->have ? ((g)->have--, (g)->pos++, *((g)->next)++) : gzgetc(g)) +#else +# define gzgetc(g) \ + ((g)->have ? ((g)->have--, (g)->pos++, *((g)->next)++) : gzgetc(g)) +#endif + +/* provide 64-bit offset functions if _LARGEFILE64_SOURCE defined, and/or + * change the regular functions to 64 bits if _FILE_OFFSET_BITS is 64 (if + * both are true, the application gets the *64 functions, and the regular + * functions are changed to 64 bits) -- in case these are set on systems + * without large file support, _LFS64_LARGEFILE must also be true + */ +#ifdef Z_LARGE64 + ZEXTERN gzFile ZEXPORT gzopen64 OF((const char *, const char *)); + ZEXTERN z_off64_t ZEXPORT gzseek64 OF((gzFile, z_off64_t, int)); + ZEXTERN z_off64_t ZEXPORT gztell64 OF((gzFile)); + ZEXTERN z_off64_t ZEXPORT gzoffset64 OF((gzFile)); + ZEXTERN uLong ZEXPORT adler32_combine64 OF((uLong, uLong, z_off64_t)); + ZEXTERN uLong ZEXPORT crc32_combine64 OF((uLong, uLong, z_off64_t)); +#endif + +#if !defined(ZLIB_INTERNAL) && defined(Z_WANT64) +# ifdef Z_PREFIX_SET +# define z_gzopen z_gzopen64 +# define z_gzseek z_gzseek64 +# define z_gztell z_gztell64 +# define z_gzoffset z_gzoffset64 +# define z_adler32_combine z_adler32_combine64 +# define z_crc32_combine z_crc32_combine64 +# else +# define gzopen gzopen64 +# define gzseek gzseek64 +# define gztell gztell64 +# define gzoffset gzoffset64 +# define adler32_combine adler32_combine64 +# define crc32_combine crc32_combine64 +# endif +# ifndef Z_LARGE64 + ZEXTERN gzFile ZEXPORT gzopen64 OF((const char *, const char *)); + ZEXTERN z_off_t ZEXPORT gzseek64 OF((gzFile, z_off_t, int)); + ZEXTERN z_off_t ZEXPORT gztell64 OF((gzFile)); + ZEXTERN z_off_t ZEXPORT gzoffset64 OF((gzFile)); + ZEXTERN uLong ZEXPORT adler32_combine64 OF((uLong, uLong, z_off_t)); + ZEXTERN uLong ZEXPORT crc32_combine64 OF((uLong, uLong, z_off_t)); +# endif +#else + ZEXTERN gzFile ZEXPORT gzopen OF((const char *, const char *)); + ZEXTERN z_off_t ZEXPORT gzseek OF((gzFile, z_off_t, int)); + ZEXTERN z_off_t ZEXPORT gztell OF((gzFile)); + ZEXTERN z_off_t ZEXPORT gzoffset OF((gzFile)); + ZEXTERN uLong ZEXPORT adler32_combine OF((uLong, uLong, z_off_t)); + ZEXTERN uLong ZEXPORT crc32_combine OF((uLong, uLong, z_off_t)); +#endif + +#else /* Z_SOLO */ + + ZEXTERN uLong ZEXPORT adler32_combine OF((uLong, uLong, z_off_t)); + ZEXTERN uLong ZEXPORT crc32_combine OF((uLong, uLong, z_off_t)); + +#endif /* !Z_SOLO */ + +/* hack for buggy compilers */ +#if !defined(ZUTIL_H) && !defined(NO_DUMMY_DECL) + struct internal_state {int dummy;}; +#endif + +/* undocumented functions */ +ZEXTERN const char * ZEXPORT zError OF((int)); +ZEXTERN int ZEXPORT inflateSyncPoint OF((z_streamp)); +ZEXTERN const z_crc_t FAR * ZEXPORT get_crc_table OF((void)); +ZEXTERN int ZEXPORT inflateUndermine OF((z_streamp, int)); +ZEXTERN int ZEXPORT inflateResetKeep OF((z_streamp)); +ZEXTERN int ZEXPORT deflateResetKeep OF((z_streamp)); +#if defined(_WIN32) && !defined(Z_SOLO) +ZEXTERN gzFile ZEXPORT gzopen_w OF((const wchar_t *path, + const char *mode)); +#endif +#if defined(STDC) || defined(Z_HAVE_STDARG_H) +# ifndef Z_SOLO +ZEXTERN int ZEXPORTVA gzvprintf Z_ARG((gzFile file, + const char *format, + va_list va)); +# endif +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* ZLIB_H */ diff --git a/libs/zlib/zlib32d.lib b/libs/zlib/zlib32d.lib new file mode 100644 index 0000000..c6bdd2e Binary files /dev/null and b/libs/zlib/zlib32d.lib differ diff --git a/libs/zlib/zlib32r.lib b/libs/zlib/zlib32r.lib new file mode 100644 index 0000000..7c09665 Binary files /dev/null and b/libs/zlib/zlib32r.lib differ diff --git a/libs/zlib/zlib64d.lib b/libs/zlib/zlib64d.lib new file mode 100644 index 0000000..d5970f3 Binary files /dev/null and b/libs/zlib/zlib64d.lib differ diff --git a/libs/zlib/zlib64r.lib b/libs/zlib/zlib64r.lib new file mode 100644 index 0000000..83db6c0 Binary files /dev/null and b/libs/zlib/zlib64r.lib differ diff --git a/libs/zlib/zutil.h b/libs/zlib/zutil.h new file mode 100644 index 0000000..24ab06b --- /dev/null +++ b/libs/zlib/zutil.h @@ -0,0 +1,253 @@ +/* zutil.h -- internal interface and configuration of the compression library + * Copyright (C) 1995-2013 Jean-loup Gailly. + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* WARNING: this file should *not* be used by applications. It is + part of the implementation of the compression library and is + subject to change. Applications should only use zlib.h. + */ + +/* @(#) $Id$ */ + +#ifndef ZUTIL_H +#define ZUTIL_H + +#ifdef HAVE_HIDDEN +# define ZLIB_INTERNAL __attribute__((visibility ("hidden"))) +#else +# define ZLIB_INTERNAL +#endif + +#include "zlib.h" + +#if defined(STDC) && !defined(Z_SOLO) +# if !(defined(_WIN32_WCE) && defined(_MSC_VER)) +# include +# endif +# include +# include +#endif + +#ifdef Z_SOLO + typedef long ptrdiff_t; /* guess -- will be caught if guess is wrong */ +#endif + +#ifndef local +# define local static +#endif +/* compile with -Dlocal if your debugger can't find static symbols */ + +typedef unsigned char uch; +typedef uch FAR uchf; +typedef unsigned short ush; +typedef ush FAR ushf; +typedef unsigned long ulg; + +extern z_const char * const z_errmsg[10]; /* indexed by 2-zlib_error */ +/* (size given to avoid silly warnings with Visual C++) */ + +#define ERR_MSG(err) z_errmsg[Z_NEED_DICT-(err)] + +#define ERR_RETURN(strm,err) \ + return (strm->msg = ERR_MSG(err), (err)) +/* To be used only when the state is known to be valid */ + + /* common constants */ + +#ifndef DEF_WBITS +# define DEF_WBITS MAX_WBITS +#endif +/* default windowBits for decompression. MAX_WBITS is for compression only */ + +#if MAX_MEM_LEVEL >= 8 +# define DEF_MEM_LEVEL 8 +#else +# define DEF_MEM_LEVEL MAX_MEM_LEVEL +#endif +/* default memLevel */ + +#define STORED_BLOCK 0 +#define STATIC_TREES 1 +#define DYN_TREES 2 +/* The three kinds of block type */ + +#define MIN_MATCH 3 +#define MAX_MATCH 258 +/* The minimum and maximum match lengths */ + +#define PRESET_DICT 0x20 /* preset dictionary flag in zlib header */ + + /* target dependencies */ + +#if defined(MSDOS) || (defined(WINDOWS) && !defined(WIN32)) +# define OS_CODE 0x00 +# ifndef Z_SOLO +# if defined(__TURBOC__) || defined(__BORLANDC__) +# if (__STDC__ == 1) && (defined(__LARGE__) || defined(__COMPACT__)) + /* Allow compilation with ANSI keywords only enabled */ + void _Cdecl farfree( void *block ); + void *_Cdecl farmalloc( unsigned long nbytes ); +# else +# include +# endif +# else /* MSC or DJGPP */ +# include +# endif +# endif +#endif + +#ifdef AMIGA +# define OS_CODE 0x01 +#endif + +#if defined(VAXC) || defined(VMS) +# define OS_CODE 0x02 +# define F_OPEN(name, mode) \ + fopen((name), (mode), "mbc=60", "ctx=stm", "rfm=fix", "mrs=512") +#endif + +#if defined(ATARI) || defined(atarist) +# define OS_CODE 0x05 +#endif + +#ifdef OS2 +# define OS_CODE 0x06 +# if defined(M_I86) && !defined(Z_SOLO) +# include +# endif +#endif + +#if defined(MACOS) || defined(TARGET_OS_MAC) +# define OS_CODE 0x07 +# ifndef Z_SOLO +# if defined(__MWERKS__) && __dest_os != __be_os && __dest_os != __win32_os +# include /* for fdopen */ +# else +# ifndef fdopen +# define fdopen(fd,mode) NULL /* No fdopen() */ +# endif +# endif +# endif +#endif + +#ifdef TOPS20 +# define OS_CODE 0x0a +#endif + +#ifdef WIN32 +# ifndef __CYGWIN__ /* Cygwin is Unix, not Win32 */ +# define OS_CODE 0x0b +# endif +#endif + +#ifdef __50SERIES /* Prime/PRIMOS */ +# define OS_CODE 0x0f +#endif + +#if defined(_BEOS_) || defined(RISCOS) +# define fdopen(fd,mode) NULL /* No fdopen() */ +#endif + +#if (defined(_MSC_VER) && (_MSC_VER > 600)) && !defined __INTERIX +# if defined(_WIN32_WCE) +# define fdopen(fd,mode) NULL /* No fdopen() */ +# ifndef _PTRDIFF_T_DEFINED + typedef int ptrdiff_t; +# define _PTRDIFF_T_DEFINED +# endif +# else +# define fdopen(fd,type) _fdopen(fd,type) +# endif +#endif + +#if defined(__BORLANDC__) && !defined(MSDOS) + #pragma warn -8004 + #pragma warn -8008 + #pragma warn -8066 +#endif + +/* provide prototypes for these when building zlib without LFS */ +#if !defined(_WIN32) && \ + (!defined(_LARGEFILE64_SOURCE) || _LFS64_LARGEFILE-0 == 0) + ZEXTERN uLong ZEXPORT adler32_combine64 OF((uLong, uLong, z_off_t)); + ZEXTERN uLong ZEXPORT crc32_combine64 OF((uLong, uLong, z_off_t)); +#endif + + /* common defaults */ + +#ifndef OS_CODE +# define OS_CODE 0x03 /* assume Unix */ +#endif + +#ifndef F_OPEN +# define F_OPEN(name, mode) fopen((name), (mode)) +#endif + + /* functions */ + +#if defined(pyr) || defined(Z_SOLO) +# define NO_MEMCPY +#endif +#if defined(SMALL_MEDIUM) && !defined(_MSC_VER) && !defined(__SC__) + /* Use our own functions for small and medium model with MSC <= 5.0. + * You may have to use the same strategy for Borland C (untested). + * The __SC__ check is for Symantec. + */ +# define NO_MEMCPY +#endif +#if defined(STDC) && !defined(HAVE_MEMCPY) && !defined(NO_MEMCPY) +# define HAVE_MEMCPY +#endif +#ifdef HAVE_MEMCPY +# ifdef SMALL_MEDIUM /* MSDOS small or medium model */ +# define zmemcpy _fmemcpy +# define zmemcmp _fmemcmp +# define zmemzero(dest, len) _fmemset(dest, 0, len) +# else +# define zmemcpy memcpy +# define zmemcmp memcmp +# define zmemzero(dest, len) memset(dest, 0, len) +# endif +#else + void ZLIB_INTERNAL zmemcpy OF((Bytef* dest, const Bytef* source, uInt len)); + int ZLIB_INTERNAL zmemcmp OF((const Bytef* s1, const Bytef* s2, uInt len)); + void ZLIB_INTERNAL zmemzero OF((Bytef* dest, uInt len)); +#endif + +/* Diagnostic functions */ +#ifdef DEBUG +# include + extern int ZLIB_INTERNAL z_verbose; + extern void ZLIB_INTERNAL z_error OF((char *m)); +# define Assert(cond,msg) {if(!(cond)) z_error(msg);} +# define Trace(x) {if (z_verbose>=0) fprintf x ;} +# define Tracev(x) {if (z_verbose>0) fprintf x ;} +# define Tracevv(x) {if (z_verbose>1) fprintf x ;} +# define Tracec(c,x) {if (z_verbose>0 && (c)) fprintf x ;} +# define Tracecv(c,x) {if (z_verbose>1 && (c)) fprintf x ;} +#else +# define Assert(cond,msg) +# define Trace(x) +# define Tracev(x) +# define Tracevv(x) +# define Tracec(c,x) +# define Tracecv(c,x) +#endif + +#ifndef Z_SOLO + voidpf ZLIB_INTERNAL zcalloc OF((voidpf opaque, unsigned items, + unsigned size)); + void ZLIB_INTERNAL zcfree OF((voidpf opaque, voidpf ptr)); +#endif + +#define ZALLOC(strm, items, size) \ + (*((strm)->zalloc))((strm)->opaque, (items), (size)) +#define ZFREE(strm, addr) (*((strm)->zfree))((strm)->opaque, (voidpf)(addr)) +#define TRY_FREE(s, p) {if (p) ZFREE(s, p);} + +/* Reverse the bytes in a 32-bit value */ +#define ZSWAP32(q) ((((q) >> 24) & 0xff) + (((q) >> 8) & 0xff00) + \ + (((q) & 0xff00) << 8) + (((q) & 0xff) << 24)) + +#endif /* ZUTIL_H */ diff --git a/src/app.cpp b/src/app.cpp new file mode 100644 index 0000000..b5ac77e --- /dev/null +++ b/src/app.cpp @@ -0,0 +1,36 @@ +#include "app.h" +#include +#include "wizard.h" +#include "pages/program.h" + +Application::Application(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) + : wizard_(nullptr) + , data_(this) +{ + INITCOMMONCONTROLSEX iccex; + iccex.dwSize = sizeof iccex; + iccex.dwICC = ICC_STANDARD_CLASSES | ICC_PROGRESS_CLASS | + ICC_BAR_CLASSES | ICC_TREEVIEW_CLASSES | ICC_LISTVIEW_CLASSES | + ICC_TAB_CLASSES | ICC_UPDOWN_CLASS | ICC_DATE_CLASSES; + InitCommonControlsEx(&iccex); + OleInitialize(NULL); + + wizard_ = new Wizard(this); + wizard_->setPage(new ProgramPage(wizard_)); +} + +Application::~Application() { + delete wizard_; + + OleFlushClipboard(); + OleUninitialize(); +} + +int Application::run() { + MSG msg; + while (GetMessage(&msg, NULL, 0, 0)) { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + return msg.wParam; +} diff --git a/src/app.h b/src/app.h new file mode 100644 index 0000000..9b53e63 --- /dev/null +++ b/src/app.h @@ -0,0 +1,28 @@ +#pragma once + +#include +#include +#include "frameui/window.h" +#include "ngdp.h" +#include "data.h" + +class Wizard; + +class Application { +public: + Application(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow); + ~Application(); + + int run(); + + ProgramData& data() { + return data_; + } + Wizard* wizard() { + return wizard_; + } + +private: + ProgramData data_; + Wizard* wizard_; +}; diff --git a/src/base/checksum.cpp b/src/base/checksum.cpp new file mode 100644 index 0000000..520ddfc --- /dev/null +++ b/src/base/checksum.cpp @@ -0,0 +1,223 @@ +#include "checksum.h" + +uint32 update_crc(uint32 crc, void const* vbuf, uint32 length) { + static uint32 crc_table[256]; + static bool table_computed = false; + uint8 const* buf = (uint8*)vbuf; + if (!table_computed) { + for (uint32 i = 0; i < 256; i++) { + uint32 c = i; + for (int k = 0; k < 8; k++) { + if (c & 1) { + c = 0xEDB88320L ^ (c >> 1); + } else { + c = c >> 1; + } + } + crc_table[i] = c; + } + table_computed = true; + } + for (uint32 i = 0; i < length; i++) { + crc = crc_table[(crc ^ buf[i]) & 0xFF] ^ (crc >> 8); + } + return crc; +} +uint32 crc32(void const* buf, uint32 length) { + return ~update_crc(0xFFFFFFFF, buf, length); +} +uint32 crc32(std::string const& str) { + return ~update_crc(0xFFFFFFFF, str.c_str(), str.length()); +} + +////////////////////////////////////////////////////////////////// + +static const uint32 MD5_R[64] = { + 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, + 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, + 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, + 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21 +}; +static uint32 MD5_K[64] = { + 0xD76AA478, 0xE8C7B756, 0x242070DB, 0xC1BDCEEE, + 0xF57C0FAF, 0x4787C62A, 0xA8304613, 0xFD469501, + 0x698098D8, 0x8B44F7AF, 0xFFFF5BB1, 0x895CD7BE, + 0x6B901122, 0xFD987193, 0xA679438E, 0x49B40821, + 0xF61E2562, 0xC040B340, 0x265E5A51, 0xE9B6C7AA, + 0xD62F105D, 0x02441453, 0xD8A1E681, 0xE7D3FBC8, + 0x21E1CDE6, 0xC33707D6, 0xF4D50D87, 0x455A14ED, + 0xA9E3E905, 0xFCEFA3F8, 0x676F02D9, 0x8D2A4C8A, + 0xFFFA3942, 0x8771F681, 0x6D9D6122, 0xFDE5380C, + 0xA4BEEA44, 0x4BDECFA9, 0xF6BB4B60, 0xBEBFBC70, + 0x289B7EC6, 0xEAA127FA, 0xD4EF3085, 0x04881D05, + 0xD9D4D039, 0xE6DB99E5, 0x1FA27CF8, 0xC4AC5665, + 0xF4292244, 0x432AFF97, 0xAB9423A7, 0xFC93A039, + 0x655B59C3, 0x8F0CCC92, 0xFFEFF47D, 0x85845DD1, + 0x6FA87E4F, 0xFE2CE6E0, 0xA3014314, 0x4E0811A1, + 0xF7537E82, 0xBD3AF235, 0x2AD7D2BB, 0xEB86D391, +}; + +MD5::MD5() { + digest[0] = 0x67452301; + digest[1] = 0xEFCDAB89; + digest[2] = 0x98BADCFE; + digest[3] = 0x10325476; + length = 0; + bufSize = 0; +} +void MD5::process(void const* _buf, uint32 size) { + uint8 const* buf = (uint8*)_buf; + while (size) { + uint32 cur = 64 - bufSize; + if (cur > size) cur = size; + memcpy(buffer + bufSize, buf, cur); + bufSize += cur; + length += cur; + size -= cur; + buf += cur; + if (bufSize == 64) run(); + } +} +void MD5::finish(void* _digest) { + buffer[bufSize++] = 0x80; + if (bufSize > 56) { + if (bufSize < 64) memset(buffer + bufSize, 0, 64 - bufSize); + run(); + } + if (bufSize < 56) memset(buffer + bufSize, 0, 56 - bufSize); + *(uint64*)(buffer + 56) = uint64(length) * 8; + run(); + memcpy(_digest, digest, sizeof digest); +} +std::string MD5::format(void const* digest) +{ + std::string result(32, ' '); + uint8 const* d = (uint8*)digest; + for (int i = 0; i < DIGEST_SIZE; i++) { + sprintf(&result[i * 2], "%02x", d[i]); + } + return result; +} +static inline uint32 rot(uint32 x, int k) { + return (x << k) | (x >> (32 - k)); +} +void MD5::run() { + uint32* m = (uint32*)buffer; + uint32 a = digest[0]; + uint32 b = digest[1]; + uint32 c = digest[2]; + uint32 d = digest[3]; + for (int i = 0; i < 64; i++) { + uint32 f, g; + if (i < 16) f = (b & c) | ((~b) & d), g = i; + else if (i < 32) f = (d & b) | ((~d) & c), g = (i * 5 + 1) & 0x0F; + else if (i < 48) f = b ^ c ^ d, g = (i * 3 + 5) & 0x0F; + else f = c ^ (b | (~d)), g = (i * 7) & 0x0F; + uint32 temp = d; + d = c; + c = b; + b += rot(a + f + MD5_K[i] + m[g], MD5_R[i]); + a = temp; + } + digest[0] += a; + digest[1] += b; + digest[2] += c; + digest[3] += d; + bufSize = 0; +} + +uint64 jenkins(void const* buf, uint32 length) { + uint32 a, b, c; + a = b = c = 0xDEADBEEF + length + 2; + c += 1; + + uint32* k = (uint32*)buf; + while (length > 12) { + a += k[0]; + b += k[1]; + c += k[2]; + + a -= c; a ^= rot(c, 4); c += b; + b -= a; b ^= rot(a, 6); a += c; + c -= b; c ^= rot(b, 8); b += a; + a -= c; a ^= rot(c, 16); c += b; + b -= a; b ^= rot(a, 19); a += c; + c -= b; c ^= rot(b, 4); b += a; + + length -= 12; + k += 3; + } + + switch (length) { + case 12: c += k[2]; b += k[1]; a += k[0]; break; + case 11: c += k[2] & 0xFFFFFF; b += k[1]; a += k[0]; break; + case 10: c += k[2] & 0xFFFF; b += k[1]; a += k[0]; break; + case 9: c += k[2] & 0xFF; b += k[1]; a += k[0]; break; + case 8: b += k[1]; a += k[0]; break; + case 7: b += k[1] & 0xFFFFFF; a += k[0]; break; + case 6: b += k[1] & 0xFFFF; a += k[0]; break; + case 5: b += k[1] & 0xFF; a += k[0]; break; + case 4: a += k[0]; break; + case 3: a += k[0] & 0xFFFFFF; break; + case 2: a += k[0] & 0xFFFF; break; + case 1: a += k[0] & 0xFF; break; + case 0: return (uint64(b) << 32) | uint64(c); + } + + c ^= b; c -= rot(b, 14); + a ^= c; a -= rot(c, 11); + b ^= a; b -= rot(a, 25); + c ^= b; c -= rot(b, 16); + a ^= c; a -= rot(c, 4); + b ^= a; b -= rot(a, 14); + c ^= b; c -= rot(b, 24); + + return (uint64(b) << 32) | uint64(c); +} +uint32 hashlittle(void const* buf, uint32 length, uint32 initval) { + uint32 a, b, c; + a = b = c = 0xDEADBEEF + length + initval; + + uint32* k = (uint32*)buf; + while (length > 12) { + a += k[0]; + b += k[1]; + c += k[2]; + + a -= c; a ^= rot(c, 4); c += b; + b -= a; b ^= rot(a, 6); a += c; + c -= b; c ^= rot(b, 8); b += a; + a -= c; a ^= rot(c, 16); c += b; + b -= a; b ^= rot(a, 19); a += c; + c -= b; c ^= rot(b, 4); b += a; + + length -= 12; + k += 3; + } + + switch (length) { + case 12: c += k[2]; b += k[1]; a += k[0]; break; + case 11: c += k[2] & 0xFFFFFF; b += k[1]; a += k[0]; break; + case 10: c += k[2] & 0xFFFF; b += k[1]; a += k[0]; break; + case 9: c += k[2] & 0xFF; b += k[1]; a += k[0]; break; + case 8: b += k[1]; a += k[0]; break; + case 7: b += k[1] & 0xFFFFFF; a += k[0]; break; + case 6: b += k[1] & 0xFFFF; a += k[0]; break; + case 5: b += k[1] & 0xFF; a += k[0]; break; + case 4: a += k[0]; break; + case 3: a += k[0] & 0xFFFFFF; break; + case 2: a += k[0] & 0xFFFF; break; + case 1: a += k[0] & 0xFF; break; + case 0: return c; + } + + c ^= b; c -= rot(b, 14); + a ^= c; a -= rot(c, 11); + b ^= a; b -= rot(a, 25); + c ^= b; c -= rot(b, 16); + a ^= c; a -= rot(c, 4); + b ^= a; b -= rot(a, 14); + c ^= b; c -= rot(b, 24); + + return c; +} diff --git a/src/base/checksum.h b/src/base/checksum.h new file mode 100644 index 0000000..d7db8e4 --- /dev/null +++ b/src/base/checksum.h @@ -0,0 +1,33 @@ +#pragma once + +#include "base/types.h" +#include + +uint32 update_crc(uint32 crc, void const* vbuf, uint32 length); +uint32 crc32(void const* buf, uint32 length); +uint32 crc32(std::string const& str); + +class MD5 { + uint8 buffer[64]; + uint32 digest[4]; + uint64 length; + uint32 bufSize; + void run(); +public: + enum { DIGEST_SIZE = 16 }; + + MD5(); + void process(void const* buf, uint32 size); + void finish(void* digest); + + static std::string format(void const* digest); + + static void checksum(void const* buf, uint32 size, void* digest) { + MD5 md5; + md5.process(buf, size); + md5.finish(digest); + } +}; + +uint64 jenkins(void const* buf, uint32 length); +uint32 hashlittle(void const* buf, uint32 length, uint32 initval); diff --git a/src/base/common.cpp b/src/base/common.cpp new file mode 100644 index 0000000..ddf1384 --- /dev/null +++ b/src/base/common.cpp @@ -0,0 +1,127 @@ +#include +#include "common.h" + +uint32 RefCounted::addref() { + return InterlockedIncrement(&ref_); +} +uint32 RefCounted::release() { + if (!this) { + return 0; + } + uint32 result = InterlockedDecrement(&ref_); + if (!result) { + delete this; + } + return result; +} + +void _qmemset(uint32* mem, uint32 fill, uint32 count) { + while (count--) { + *mem++ = fill; + } +} + +#include "zlib/zlib.h" +#ifdef _WIN64 + #ifdef _DEBUG + #pragma comment(lib, "zlib/zlib64d.lib") + #else + #pragma comment(lib, "zlib/zlib64r.lib") + #endif +#else + #ifdef _DEBUG + #pragma comment(lib, "zlib/zlib32d.lib") + #else + #pragma comment(lib, "zlib/zlib32r.lib") + #endif +#endif + +uint32 gzdeflate(uint8 const* in, uint32 in_size, uint8* out, uint32* out_size) { + z_stream z; + memset(&z, 0, sizeof z); + z.next_in = const_cast(in); + z.avail_in = in_size; + z.total_in = in_size; + z.next_out = out; + z.avail_out = *out_size; + z.total_out = 0; + + memset(out, 0, *out_size); + + int result = deflateInit(&z, Z_DEFAULT_COMPRESSION); + if (result == Z_OK) { + result = deflate(&z, Z_FINISH); + *out_size = z.total_out; + deflateEnd(&z); + } + return (result == Z_STREAM_END ? 0 : -1); +} +uint32 gzencode(uint8 const* in, uint32 in_size, uint8* out, uint32* out_size) { + z_stream z; + memset(&z, 0, sizeof z); + z.next_in = const_cast(in); + z.avail_in = in_size; + z.total_in = in_size; + z.next_out = out; + z.avail_out = *out_size; + z.total_out = 0; + + int result = deflateInit2(&z, 6, Z_DEFLATED, 16 + MAX_WBITS, 8, Z_DEFAULT_STRATEGY); + if (result == Z_OK) { + result = deflate(&z, Z_FINISH); + *out_size = z.total_out; + deflateEnd(&z); + } + return ((result == Z_OK || result == Z_STREAM_END) ? 0 : 1); +} +uint32 gzinflate(uint8 const* in, uint32 in_size, uint8* out, uint32* out_size) { + z_stream z; + memset(&z, 0, sizeof z); + z.next_in = const_cast(in); + z.avail_in = in_size; + z.total_in = in_size; + z.next_out = out; + z.avail_out = *out_size; + z.total_out = 0; + + memset(out, 0, *out_size); + + int result = inflateInit(&z); + if (result == Z_OK) { + result = inflate(&z, Z_FINISH); + *out_size = z.total_out; + inflateEnd(&z); + } + return (z.avail_out == 0 ? 0 : -1); +} +uint32 gzdecode(uint8 const* in, uint32 in_size, uint8* out, uint32* out_size) { + z_stream z; + memset(&z, 0, sizeof z); + z.next_in = const_cast(in); + z.avail_in = in_size; + z.total_in = in_size; + z.next_out = out; + z.avail_out = *out_size; + z.total_out = 0; + + int result = inflateInit2(&z, 16 + MAX_WBITS); + if (result == Z_OK) { + result = inflate(&z, Z_FINISH); + *out_size = z.total_out; + deflateEnd(&z); + } + return (z.avail_out == 0 ? 0 : 1); +} + +std::string formatSize(uint64 usize) { + double size = static_cast(usize); + if (size < 100 * 1024) { + return fmtstring("%u B", static_cast(size)); + } else if (size < 10.0 * 1024 * 1024) { + return fmtstring("%u K", static_cast(size / 1024)); + } else if (size < 10.0 * 1024 * 1024 * 1024) { + return fmtstring("%.1f M", size / 1024 / 1024); + } else { + return fmtstring("%.1f G", size / 1024 / 1024 / 1024); + } +} diff --git a/src/base/common.h b/src/base/common.h new file mode 100644 index 0000000..d27c098 --- /dev/null +++ b/src/base/common.h @@ -0,0 +1,78 @@ +#pragma once + +#include "types.h" +#include +#include +#include +#include +#include +#define NOMINMAX +#include + +#include "string.h" + +class RefCounted { + uint32 ref_; +public: + RefCounted() : ref_(1) {} + virtual ~RefCounted() {} + + bool unique() const { + return ref_ == 1; + } + uint32 addref(); + uint32 release(); +}; + +void _qmemset(uint32* mem, uint32 fill, uint32 count); + +template +using Map = std::map; +typedef Map Dictionary; + +template +typename MapType::mapped_type* getptr(MapType& map, Key const& key) { + auto it = map.find(key); + return (it == map.end() ? nullptr : &it->second); +} +template +typename MapType::mapped_type const* getptr(MapType const& map, Key const& key) { + auto it = map.find(key); + return (it == map.end() ? nullptr : &it->second); +} + +uint32 gzdeflate(uint8 const* in, uint32 in_size, uint8* out, uint32* out_size); +uint32 gzencode(uint8 const* in, uint32 in_size, uint8* out, uint32* out_size); +uint32 gzinflate(uint8 const* in, uint32 in_size, uint8* out, uint32* out_size); +uint32 gzdecode(uint8 const* in, uint32 in_size, uint8* out, uint32* out_size); + +template +struct FlipTraits {}; + +template<> struct FlipTraits<1> { + typedef unsigned char T; + static T flip(T x) { return x; } +}; + +template<> struct FlipTraits<2> { + typedef unsigned short T; + static T flip(T x) { return _byteswap_ushort(x); } +}; + +template<> struct FlipTraits<4> { + typedef unsigned long T; + static T flip(T x) { return _byteswap_ulong(x); } +}; + +template<> struct FlipTraits<8> { + typedef unsigned long long T; + static T flip(T x) { return _byteswap_uint64(x); } +}; + +template +void flip(T& x) { + typedef FlipTraits Flip; + x = static_cast(Flip::flip(static_cast(x))); +} + +std::string formatSize(uint64 size); diff --git a/src/base/error.cpp b/src/base/error.cpp new file mode 100644 index 0000000..0f47450 --- /dev/null +++ b/src/base/error.cpp @@ -0,0 +1,16 @@ +#include "error.h" +#include "types.h" +#include + +Exception::Exception(char const* fmt, ...) { + va_list ap; + va_start(ap, fmt); + + uint32 len = _vscprintf(fmt, ap); + std::string dst; + dst.resize(len + 1); + vsprintf(&dst[0], fmt, ap); + dst.resize(len); + + buf_.str(dst); +} diff --git a/src/base/error.h b/src/base/error.h new file mode 100644 index 0000000..cc32977 --- /dev/null +++ b/src/base/error.h @@ -0,0 +1,31 @@ +#pragma once + +#include +#include + +class Exception { +public: + Exception(char const* fmt, ...); + Exception(Exception const& e) + : buf_(e.buf_.str()) + {} + + virtual char const* what() const throw() { + str_ = buf_.str(); + return str_.c_str(); + } + + template + inline void append(T const& t) { + buf_ << t; + } +private: + mutable std::string str_; + std::stringstream buf_; +}; + +template +static inline Exception&& operator<<(Exception&& e, T const& t) { + e.append(t); + return std::forward(e); +} diff --git a/src/base/file.cpp b/src/base/file.cpp new file mode 100644 index 0000000..77bbc6b --- /dev/null +++ b/src/base/file.cpp @@ -0,0 +1,399 @@ +#include "file.h" +#include +#include +#include + +class StdFileBuffer : public FileBuffer { + FILE* file_; +public: + StdFileBuffer(FILE* file) + : file_(file) + {} + ~StdFileBuffer() { + fclose(file_); + } + + int getc() { + return fgetc(file_); + } + void putc(int chr) { + fputc(chr, file_); + } + + uint64 tell() const { + return _ftelli64(file_); + } + void seek(int64 pos, int mode) { + _fseeki64(file_, pos, mode); + } + + size_t read(void* ptr, size_t size) { + return fread(ptr, 1, size, file_); + } + size_t write(void const* ptr, size_t size) { + return fwrite(ptr, 1, size, file_); + } +}; + +File::File(char const* name, uint32 mode) + : file_(nullptr) +{ + FILE* file = nullptr; + if (mode == READ) { + file = fopen(name, "rb"); + } else if (mode == MODIFY) { + file = fopen(name, "rb+"); + if (!file) { + mode = REWRITE; + } + } + if (mode == REWRITE) { + file = fopen(name, "wb+"); + if (!file) { + path::create(path::path(name)); + file = fopen(name, "wb+"); + } + } + if (file) { + file_ = new StdFileBuffer(file); + } +} + +void File::printf(char const* fmt, ...) { + static char buf[1024]; + + va_list ap; + va_start(ap, fmt); + + int len = _vscprintf(fmt, ap); + char* dst; + if (len < 1024) { + dst = buf; + } else { + dst = new char[len + 1]; + } + vsprintf(dst, fmt, ap); + file_->write(dst, len); + if (dst != buf) { + delete[] dst; + } +} +bool File::getline(std::string& out) { + out.clear(); + int chr; + while ((chr = file_->getc()) != EOF) { + if (chr == '\r') { + char next = file_->getc(); + if (next != '\n') { + file_->seek(-1, SEEK_CUR); + } + return true; + } + if (chr == '\n') { + return true; + } + out.push_back(chr); + } + return !out.empty(); +} +File::LineIterator File::begin() { + return LineIterator(*this); +} +File::LineIterator File::end() { + return LineIterator(); +} + +class MemFileBuffer : public FileBuffer { + uint8 const* ptr_; + uint8* clone_; + size_t pos_; + size_t size_; +public: + MemFileBuffer(uint8 const* ptr, size_t size, bool clone) + : ptr_(ptr) + , pos_(0) + , size_(size) + , clone_(nullptr) + { + if (clone) { + clone_ = new uint8[size]; + memcpy(clone_, ptr, size); + ptr_ = clone_; + } + } + ~MemFileBuffer() { + delete[] clone_; + } + + int getc() { + return (pos_ < size_ ? ptr_[pos_++] : EOF); + } + void putc(int chr) {} + + uint64 tell() const { + return pos_; + } + void seek(int64 pos, int mode) { + switch (mode) { + case SEEK_CUR: + pos += pos_; + break; + case SEEK_END: + pos += size_; + break; + } + if (pos < 0) pos = 0; + if (pos > size_) pos = size_; + pos_ = pos; + } + uint64 size() { + return size_; + } + + size_t read(void* ptr, size_t size) { + if (size + pos_ > size_) { + size = size_ - pos_; + } + if (size) { + memcpy(ptr, ptr_ + pos_, size); + pos_ += size; + } + return size; + } + size_t write(void const* ptr, size_t size) { + return 0; + } +}; + +File File::memfile(void const* ptr, size_t size, bool clone) { + return File(new MemFileBuffer((uint8*)ptr, size, clone)); +} + +class SubFileBuffer : public FileBuffer { + File file_; + uint64 start_; + uint64 end_; + uint64 pos_; +public: + SubFileBuffer(File& file, uint64 offset, uint64 size) + : file_(file) + , start_(offset) + , end_(offset + size) + , pos_(offset) + { + file.seek(offset); + } + + int getc() { + if (pos_ >= end_) return EOF; + ++pos_; + return file_.getc(); + } + void putc(int chr) {} + + uint64 tell() const { + return pos_ - start_; + } + void seek(int64 pos, int mode) { + switch (mode) { + case SEEK_SET: + pos += start_; + break; + case SEEK_CUR: + pos += pos_; + break; + case SEEK_END: + pos += end_; + break; + } + if (pos < start_) pos = start_; + if (pos > end_) pos = end_; + pos_ = pos; + file_.seek(pos_); + } + uint64 size() { + return end_ - start_; + } + + size_t read(void* ptr, size_t size) { + if (size + pos_ > end_) { + size = end_ - pos_; + } + if (size) { + size = file_.read(ptr, size); + pos_ += size; + } + return size; + } + size_t write(void const* ptr, size_t size) { + return 0; + } +}; + +File File::subfile(uint64 offset, uint64 size) { + return File(new SubFileBuffer(*this, offset, size)); +} + +class MemoryBuffer : public FileBuffer { + size_t pos_; + uint8* data_; + size_t size_; + size_t alloc_; + size_t grow_; +public: + MemoryBuffer(size_t size, size_t grow) + : alloc_(size) + , grow_(grow) + , size_(0) + , pos_(0) + { + data_ = new uint8[size]; + } + ~MemoryBuffer() { + delete[] data_; + } + + int getc() { + if (pos_ < size_) return data_[pos_++]; + return EOF; + } + + uint64 tell() const { + return pos_; + } + void seek(int64 pos, int mode) { + switch (mode) { + case SEEK_CUR: + pos += pos_; + break; + case SEEK_END: + pos += size_; + break; + } + if (pos < 0) pos = 0; + if (pos > size_) pos = size_; + pos_ = pos; + } + uint64 size() { + return size_; + } + + size_t read(void* ptr, size_t size) { + if (size + pos_ > size_) { + size = size_ - pos_; + } + if (size) { + memcpy(ptr, data_ + pos_, size); + pos_ += size; + } + return size; + } + + size_t write(void const* ptr, size_t size) { + memcpy(reserve(size), ptr, size); + return size; + } + + uint8 const* data() const { + return data_; + } + uint8* reserve(uint32 size) { + if (pos_ + size > alloc_) { + while (alloc_ < pos_ + size) { + if (alloc_ < grow_) alloc_ *= 2; + else alloc_ += grow_; + } + uint8* temp = new uint8[alloc_]; + memcpy(temp, data_, size_); + delete[] data_; + data_ = temp; + } + uint8* res = data_ + pos_; + pos_ += size; + if (pos_ > size_) size_ = pos_; + return res; + } + void resize(uint32 size) { + size_ = size; + if (pos_ > size) pos_ = size; + } +}; + +MemoryFile::MemoryFile(size_t initial, size_t grow) + : File(new MemoryBuffer(initial, grow)) +{} +uint8 const* MemoryFile::data() const { + MemoryBuffer* buffer = dynamic_cast(file_); + return (buffer ? buffer->data() : nullptr); +} +uint8* MemoryFile::reserve(uint32 size) { + MemoryBuffer* buffer = dynamic_cast(file_); + return (buffer ? buffer->reserve(size) : nullptr); +} +void MemoryFile::resize(uint32 size) { + MemoryBuffer* buffer = dynamic_cast(file_); + if (buffer) buffer->resize(size); +} +size_t MemoryFile::csize() const { + MemoryBuffer* buffer = dynamic_cast(file_); + return (buffer ? buffer->size() : 0); +} + +void File::copy(File& src, uint64 size) { + auto mem = dynamic_cast(src.file_); + if (mem) { + uint64 pos = mem->tell(); + size = std::min(size, mem->size() - pos); + write(mem->data() + pos, size); + mem->seek(size, SEEK_CUR); + } else { + uint8 buf[65536]; + while (size_t count = src.read(buf, std::min(sizeof buf, size))) { + write(buf, count); + size -= count; + } + } +} +#include "checksum.h" +void File::md5(void* digest) { + auto mem = dynamic_cast(file_); + if (mem) { + MD5::checksum(mem->data(), mem->size(), digest); + } else { + uint64 pos = tell(); + seek(0, SEEK_SET); + uint8 buf[65536]; + MD5 checksum; + while (size_t count = read(buf, sizeof buf)) { + checksum.process(buf, count); + } + checksum.finish(digest); + seek(pos, SEEK_SET); + } +} +std::string File::md5() { + uint8 digest[MD5::DIGEST_SIZE]; + md5(digest); + return MD5::format(digest); +} + +bool File::exists(char const* path) { + return GetFileAttributes(path) != INVALID_FILE_ATTRIBUTES; +} + +FileLoader::SearchResults FileLoader::search(char const* mask) { + WIN32_FIND_DATA fdata; + HANDLE hFind = FindFirstFile((root / mask).c_str(), &fdata); + SearchResults results; + if (hFind == INVALID_HANDLE_VALUE) return results; + do { + if (!strcmp(fdata.cFileName, ".") || !strcmp(fdata.cFileName, "..")) continue; + if (fdata.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { + results.folders.push_back(fdata.cFileName); + } else { + results.files.push_back(fdata.cFileName); + } + } while (FindNextFile(hFind, &fdata)); + FindClose(hFind); + return results; +} diff --git a/src/base/file.h b/src/base/file.h new file mode 100644 index 0000000..f17e2ea --- /dev/null +++ b/src/base/file.h @@ -0,0 +1,251 @@ +#pragma once + +#include +#include "common.h" +#include "path.h" +#include +#include + +class FileBuffer : public RefCounted { +public: + virtual ~FileBuffer() {} + + virtual int getc() { + uint8 chr; + if (read(&chr, 1) != 1) { + return EOF; + } + return chr; + } + virtual void putc(int chr) { + write(&chr, 1); + } + + virtual uint64 tell() const = 0; + virtual void seek(int64 pos, int mode) = 0; + virtual uint64 size() { + uint64 pos = tell(); + seek(0, SEEK_END); + uint64 res = tell(); + seek(pos, SEEK_SET); + return res; + } + + virtual size_t read(void* ptr, size_t size) = 0; + virtual size_t write(void const* ptr, size_t size) = 0; +}; + +class File { +protected: + FileBuffer* file_; +public: + File() + : file_(nullptr) + {} + File(FileBuffer* file) + : file_(file) + {} + File(File const& file) + : file_(file.file_) + { + if (file_) file_->addref(); + } + File(File&& file) + : file_(file.file_) + { + file.file_ = nullptr; + } + + enum { + READ = 0, + REWRITE = 1, + MODIFY = 2, + }; + + File(char const* name, uint32 mode = READ); + File(std::string const& name, uint32 mode = READ) + : File(name.c_str(), mode) + {} + ~File() { + file_->release(); + } + void release() { + file_->release(); + file_ = nullptr; + } + + File& operator=(File const& file) { + if (file_ == file.file_) { + return *this; + } + file_->release(); + file_ = file.file_; + if (file_) file_->addref(); + return *this; + } + File& operator=(File&& file) { + if (file_ == file.file_) { + return *this; + } + file_->release(); + file_ = file.file_; + file.file_ = nullptr; + return *this; + } + + operator bool() const { + return file_ != nullptr; + } + + int getc() { + return file_->getc(); + } + void putc(int chr) { + file_->putc(chr); + } + + void seek(int64 pos, int mode = SEEK_SET) { + file_->seek(pos, mode); + } + uint64 tell() const { + return file_->tell(); + } + uint64 size() { + return file_->size(); + } + + size_t read(void* dst, size_t size) { + return file_->read(dst, size); + } + template + T read() { + T x; + file_->read(&x, sizeof(T)); + return x; + } + uint8 read8() { + uint8 x; + file_->read(&x, 1); + return x; + } + uint16 read16(bool big = false) { + uint16 x; + file_->read(&x, 2); + if (big) x = _byteswap_ushort(x); + return x; + } + uint32 read32(bool big = false) { + uint32 x; + file_->read(&x, 4); + if (big) x = _byteswap_ulong(x); + return x; + } + uint64 read64(bool big = false) { + uint64 x; + file_->read(&x, 8); + if (big) x = _byteswap_uint64(x); + return x; + } + + size_t write(void const* ptr, size_t size) { + return file_->write(ptr, size); + } + template + bool write(T const& x) { + return file_->write(&x, sizeof(T)) == sizeof(T); + } + bool write8(uint8 x) { + return file_->write(&x, 1) == 1; + } + bool write16(uint16 x, bool big = false) { + if (big) x = _byteswap_ushort(x); + return file_->write(&x, 2) == 2; + } + bool write32(uint32 x, bool big = false) { + if (big) x = _byteswap_ulong(x); + return file_->write(&x, 4) == 4; + } + bool write64(uint64 x, bool big = false) { + if (big) x = _byteswap_uint64(x); + return file_->write(&x, 8) == 8; + } + + void printf(char const* fmt, ...); + + bool getline(std::string& line); + + class LineIterator; + LineIterator begin(); + LineIterator end(); + + static File memfile(void const* ptr, size_t size, bool clone = false); + File subfile(uint64 offset, uint64 size); + + void copy(File& src, uint64 size = max_uint64); + void md5(void* digest); + std::string md5(); + + static bool exists(char const* path); + static bool exists(std::string const& path) { + return exists(path.c_str()); + } +}; + +class MemoryFile : public File { +public: + MemoryFile(size_t initial = 16384, size_t grow = (1 << 20)); + uint8 const* data() const; + size_t csize() const; + uint8* reserve(uint32 size); + void resize(uint32 size); +}; + +class File::LineIterator { + friend class File; + File file_; + std::string line_; + LineIterator(File& file) { + if (file.getline(line_)) { + file_ = file; + } + } +public: + LineIterator() {} + + std::string const& operator*() { + return line_; + } + std::string const* operator->() { + return &line_; + } + + bool operator!=(LineIterator const& it) const { + return file_ != it.file_; + } + + LineIterator& operator++() { + if (!file_.getline(line_)) { + file_.release(); + } + return *this; + } +}; + +class FileLoader { + std::string root; +public: + FileLoader(std::string const& root = path::root()) + : root(root) + { + } + + struct SearchResults { + std::vector files; + std::vector folders; + }; + + File load(char const* name) { + return File(root / name); + } + SearchResults search(char const* mask); +}; diff --git a/src/base/functor.h b/src/base/functor.h new file mode 100644 index 0000000..987d368 --- /dev/null +++ b/src/base/functor.h @@ -0,0 +1,77 @@ +#pragma once + +template +class Functor { + class FunctionBase { + public: + virtual ~FunctionBase() {} + virtual Ret run(Args... args) const = 0; + }; + template + class Function : public FunctionBase { + F const& func_; + public: + Function(F const& f) + : func_(f) + {} + Ret run(Args... args) const { + return func_(args...); + } + }; + FunctionBase* func_; +public: + template + Functor(F const& f) + : func_(new Function(f)) + {} + Functor(Functor const& f) = delete; + ~Functor() { + delete func_; + } + Functor(Functor&& f) + : func_(f.func_) + { + f.func_ = nullptr; + } + Ret operator()(Args... args) const { + return func_->run(args...); + } +}; + +template +class FunctorNoRet { + class FunctionBase { + public: + virtual ~FunctionBase() {} + virtual void run(Args... args) const = 0; + }; + template + class Function : public FunctionBase { + F const& func_; + public: + Function(F const& f) + : func_(f) + {} + void run(Args... args) const { + return func_(args...); + } + }; + FunctionBase* func_; +public: + template + FunctorNoRet(F const& f) + : func_(new Function(f)) + {} + FunctorNoRet(FunctorNoRet const& f) = delete; + ~FunctorNoRet() { + delete func_; + } + FunctorNoRet(FunctorNoRet&& f) + : func_(f.func_) + { + f.func_ = nullptr; + } + void operator()(Args... args) const { + func_->run(args...); + } +}; diff --git a/src/base/http.cpp b/src/base/http.cpp new file mode 100644 index 0000000..3aedeed --- /dev/null +++ b/src/base/http.cpp @@ -0,0 +1,216 @@ +#define NOMINMAX +#include "http.h" +#include "common.h" +#include "types.h" +#include +#pragma comment(lib, "winhttp.lib") + +HttpRequest::SessionHolder::~SessionHolder() { + if (request) WinHttpCloseHandle(request); + if (connect) WinHttpCloseHandle(connect); + if (session) WinHttpCloseHandle(session); +} + +HttpRequest::HttpRequest(std::string const& url, RequestType type) + : type_(type) + , handles_(new SessionHolder) +{ + std::wstring url16 = utf8_to_utf16(url); + URL_COMPONENTS urlComp; + memset(&urlComp, 0, sizeof urlComp); + urlComp.dwStructSize = sizeof urlComp; + urlComp.dwSchemeLength = -1; + urlComp.dwHostNameLength = -1; + urlComp.dwUrlPathLength = -1; + urlComp.dwExtraInfoLength = -1; + + if (!WinHttpCrackUrl(url16.c_str(), url.size(), 0, &urlComp)) { + return; + } + + std::wstring host(urlComp.lpszHostName, urlComp.dwHostNameLength); + std::wstring path(urlComp.lpszUrlPath, urlComp.dwUrlPathLength); + + handles_->session = WinHttpOpen(L"ShrineTips", WINHTTP_ACCESS_TYPE_DEFAULT_PROXY, NULL, NULL, 0); + if (!handles_->session) return; + handles_->connect = WinHttpConnect(handles_->session, host.c_str(), urlComp.nPort, 0); + if (!handles_->connect) return; + handles_->request = WinHttpOpenRequest( + handles_->connect, + type == GET ? L"GET" : L"POST", + path.c_str(), + L"HTTP/1.1", + WINHTTP_NO_REFERER, + WINHTTP_DEFAULT_ACCEPT_TYPES, + (urlComp.nScheme == INTERNET_SCHEME_HTTPS ? WINHTTP_FLAG_SECURE : 0) | WINHTTP_FLAG_BYPASS_PROXY_CACHE); +} + +void HttpRequest::addHeader(std::string const& header) { + headers_.append(utf8_to_utf16(header)); + headers_.append(L"\r\n"); +} + +void HttpRequest::addHeader(std::string const& name, std::string const& value) { + addHeader(name + ": " + value); +} + +static std::string urlencode(std::string const& str) { + std::string dst; + for (unsigned char c : str) { + if (isalnum(c)) { + dst.push_back(c); + } else if (c == ' ') { + dst.push_back('+'); + } else { + char hex[4]; + sprintf_s(hex, sizeof hex, "%%%02X", c); + dst.append(hex); + } + } + return dst; +} + +void HttpRequest::addData(std::string const& key, std::string const& value) { + if (!post_.empty()) post_.push_back('&'); + post_.append(urlencode(key)); + post_.push_back('='); + post_.append(urlencode(value)); +} + +bool HttpRequest::send() { + if (!handles_->request) return false; + if (!WinHttpSendRequest(handles_->request, + headers_.empty() ? nullptr : headers_.c_str(), headers_.size(), + post_.empty() ? nullptr : &post_[0], post_.size(), post_.size(), 0)) { + return false; + } + return WinHttpReceiveResponse(handles_->request, NULL); +} + +uint32 HttpRequest::status() { + if (!handles_->request) return 0; + + DWORD statusCode = 0; + uint32 size = sizeof statusCode; + WinHttpQueryHeaders(handles_->request, WINHTTP_QUERY_STATUS_CODE | WINHTTP_QUERY_FLAG_NUMBER, WINHTTP_HEADER_NAME_BY_INDEX, + &statusCode, &size, WINHTTP_NO_HEADER_INDEX); + return statusCode; +} + + +class HttpBuffer : public FileBuffer { + std::shared_ptr handles_; + size_t size_; + uint8* data_; + size_t pos_; + size_t loaded_; +public: + HttpBuffer(std::shared_ptr const& handles, size_t size) + : handles_(handles) + , size_(size) + , pos_(0) + , loaded_(0) + { + data_ = new uint8[size]; + } + ~HttpBuffer() { + delete[] data_; + } + + int getc() { + if (pos_ < loaded_) { + return data_[pos_++]; + } else if (pos_ >= size_) { + return EOF; + } else { + uint8 chr; + read(&chr, 1); + return chr; + } + } + + uint64 tell() const { + return pos_; + } + void seek(int64 pos, int mode) { + switch (mode) { + case SEEK_CUR: + pos += pos_; + break; + case SEEK_END: + pos += size_; + break; + } + if (pos < 0) pos = 0; + if (pos > size_) pos = size_; + pos_ = pos; + } + uint64 size() { + return size_; + } + + size_t read(void* ptr, size_t size) { + if (size + pos_ > size_) { + size = size_ - pos_; + } + while (pos_ + size > loaded_) { + DWORD nsize = 0, nread; + if (!WinHttpQueryDataAvailable(handles_->request, &nsize) || !nsize) break; + WinHttpReadData(handles_->request, data_ + loaded_, std::min(nsize, size_ - loaded_), &nread); + loaded_ += nread; + } + if (size + pos_ > loaded_) { + size = loaded_ - pos_; + } + if (size) { + memcpy(ptr, data_ + pos_, size); + pos_ += size; + } + return size; + } + + size_t write(void const* ptr, size_t size) { + return 0; + } +}; + +File HttpRequest::response() { + if (!handles_->request) return File(); + + DWORD contentLength = 0; + DWORD size = (sizeof contentLength); + WinHttpQueryHeaders(handles_->request, WINHTTP_QUERY_CONTENT_LENGTH | WINHTTP_QUERY_FLAG_NUMBER, WINHTTP_HEADER_NAME_BY_INDEX, + &contentLength, &size, WINHTTP_NO_HEADER_INDEX); + + return File(new HttpBuffer(handles_, contentLength)); +} + +File HttpRequest::get(std::string const& url) { + HttpRequest request(url); + if (!request.send() || request.status() != 200) return File(); + return request.response(); +} + +std::map HttpRequest::headers() { + std::map result; + if (!handles_->request) return result; + DWORD size; + WinHttpQueryHeaders(handles_->request, WINHTTP_QUERY_RAW_HEADERS, WINHTTP_HEADER_NAME_BY_INDEX, nullptr, &size, WINHTTP_NO_HEADER_INDEX); + if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) return result; + std::vector raw((size + 1) / sizeof(wchar_t)); + WinHttpQueryHeaders(handles_->request, WINHTTP_QUERY_RAW_HEADERS, WINHTTP_HEADER_NAME_BY_INDEX, &raw[0], &size, WINHTTP_NO_HEADER_INDEX); + std::wstring cur; + for (wchar_t wc : raw) { + if (wc) { + cur.push_back(wc); + } else if (!cur.empty()) { + std::string line = utf16_to_utf8(cur); + cur.clear(); + + size_t colon = line.find(':'); + if (colon == std::string::npos) continue; + result.emplace(trim(line.substr(0, colon)), trim(line.substr(colon + 1))); + } + } + return result; +} diff --git a/src/base/http.h b/src/base/http.h new file mode 100644 index 0000000..ad60e55 --- /dev/null +++ b/src/base/http.h @@ -0,0 +1,38 @@ +#pragma once + +#include +#include +#include +#include +#include "file.h" + +class HttpRequest { +public: + enum RequestType {GET, POST}; + + HttpRequest(std::string const& url, RequestType type = GET); + + void addHeader(std::string const& name, std::string const& value); + void addHeader(std::string const& header); + void addData(std::string const& key, std::string const& value); + + bool send(); + uint32 status(); + std::map headers(); + File response(); + + static File get(std::string const& url); + +private: + struct SessionHolder { + HINTERNET session = nullptr; + HINTERNET connect = nullptr; + HINTERNET request = nullptr; + ~SessionHolder(); + }; + friend class HttpBuffer; + std::shared_ptr handles_; + RequestType type_; + std::wstring headers_; + std::string post_; +}; diff --git a/src/base/json.cpp b/src/base/json.cpp new file mode 100644 index 0000000..dcbee7d --- /dev/null +++ b/src/base/json.cpp @@ -0,0 +1,989 @@ +#include "json.h" +#include + +namespace json { + +template +struct Defaults { + static T value_; +}; +template +T Defaults::value_; + +Value::Value(Type type) + : type_(tUndefined) +{ + setType(type); +} +Value::Value(bool val) + : type_(tUndefined) +{ + setType(tBoolean); + bool_ = val; +} +Value::Value(int val) + : type_(tUndefined) +{ + setType(tInteger); + int_ = val; +} +Value::Value(unsigned int val) + : type_(tUndefined) +{ + if (val > 0x7FFFFFFF) { + setType(tNumber); + number_ = static_cast(val); + } else { + setType(tInteger); + int_ = static_cast(val); + } +} +Value::Value(sint32 val) + : type_(tUndefined) +{ + setType(tInteger); + int_ = static_cast(val); +} +Value::Value(uint32 val) + : type_(tUndefined) +{ + if (val > 0x7FFFFFFF) { + setType(tNumber); + number_ = static_cast(val); + } else { + setType(tInteger); + int_ = static_cast(val); + } +} +Value::Value(sint64 val) + : type_(tUndefined) +{ + if (val > 0x7FFFFFFFLL || val < -0x80000000LL) { + setType(tNumber); + number_ = static_cast(val); + } else { + setType(tInteger); + int_ = static_cast(val); + } +} +Value::Value(uint64 val) + : type_(tUndefined) +{ + if (val > 0x7FFFFFFFULL) { + setType(tNumber); + number_ = static_cast(val); + } else { + setType(tInteger); + int_ = static_cast(val); + } +} +Value::Value(double val) + : type_(tUndefined) +{ + setType(tNumber); + number_ = val; +} +Value::Value(std::string const& val) + : type_(tUndefined) +{ + setType(tString); + *string_ = val; +} +Value::Value(char const* val) + : type_(tUndefined) +{ + setType(tString); + *string_ = val; +} +Value::Value(Value const& rhs) +: type_(rhs.type_) +{ + switch (type_) { + case tString: + string_ = new std::string(*rhs.string_); + break; + case tObject: + map_ = new Map(*rhs.map_); + break; + case tArray: + array_ = new Array(*rhs.array_); + break; + case tInteger: + int_ = rhs.int_; + break; + case tNumber: + number_ = rhs.number_; + break; + case tBoolean: + bool_ = rhs.bool_; + break; + } +} + +Value& Value::operator=(Value const& rhs) { + clear(); + type_ = rhs.type_; + switch (type_) { + case tString: + string_ = new std::string(*rhs.string_); + break; + case tObject: + map_ = new Map(*rhs.map_); + break; + case tArray: + array_ = new Array(*rhs.array_); + break; + case tInteger: + int_ = rhs.int_; + break; + case tNumber: + number_ = rhs.number_; + break; + case tBoolean: + bool_ = rhs.bool_; + break; + } + return *this; +} + +void Value::clear() { + switch (type_) { + case tString: + delete string_; + break; + case tObject: + delete map_; + break; + case tArray: + delete array_; + break; + } + type_ = tUndefined; +} +Value& Value::setType(Type type) { + if (type == type_) return *this; + clear(); + type_ = type; + switch (type_) { + case tString: + string_ = new std::string(); + break; + case tObject: + map_ = new Map(); + break; + case tArray: + array_ = new Array(); + break; + } + return *this; +} + +// tBoolean +bool Value::getBoolean() const { + return (type_ == tBoolean ? bool_ : false); +} +Value& Value::setBoolean(bool data) { + setType(tBoolean); + bool_ = data; + return *this; +} + +std::string const& Value::getString() const { + return (type_ == tString ? *string_ : Defaults::value_); +} +Value& Value::setString(char const* data) { + setType(tString); + *string_ = data; + return *this; +} +Value& Value::setString(std::string const& data) { + setType(tString); + *string_ = data; + return *this; +} + +// tNumber +bool Value::isInteger() const { + switch (type_) { + case tInteger: return true; + case tNumber: return (int)number_ == number_; + default: return false; + } +} +int Value::getInteger() const { + if (!isInteger()) return 0; + switch (type_) { + case tInteger: return int_; + case tNumber: return static_cast(number_); + default: return 0; + } +} +double Value::getNumber() const { + switch (type_) { + case tInteger: return static_cast(int_); + case tNumber: return number_; + default: return 0; + } +} +Value& Value::setInteger(int data) { + setType(tInteger); + int_ = data; + return *this; +} +Value& Value::setNumber(double data) { + setType(tNumber); + number_ = data; + return *this; +} + +// tObject +Value::Map const& Value::getMap() const { + return (type_ == tObject ? *map_ : Defaults::value_); +} +bool Value::has(std::string const& name) const { + if (type_ != tObject) return false; + return map_->find(name) != map_->end(); +} +bool Value::has(char const* name) const { + if (type_ != tObject) return false; + return map_->find(name) != map_->end(); +} +Value const* Value::get(std::string const& name) const { + if (type_ != tObject) return nullptr; + auto it = map_->find(name); + return (it != map_->end() ? &it->second : nullptr); +} +Value* Value::get(std::string const& name) { + if (type_ != tObject) return nullptr; + auto it = map_->find(name); + return (it != map_->end() ? &it->second : nullptr); +} +Value const* Value::get(char const* name) const { + if (type_ != tObject) return nullptr; + auto it = map_->find(name); + return (it != map_->end() ? &it->second : nullptr); +} +Value* Value::get(char const* name) { + if (type_ != tObject) return nullptr; + auto it = map_->find(name); + return (it != map_->end() ? &it->second : nullptr); +} +Value& Value::insert(std::string const& name, Value const& data) { + setType(tObject); + return (*map_)[name] = data; +} +Value& Value::insert(char const* name, Value const& data) { + setType(tObject); + return (*map_)[name] = data; +} +void Value::remove(std::string const& name) { + if (type_ == tObject) map_->erase(name); +} +void Value::remove(char const* name) { + if (type_ == tObject) map_->erase(name); +} +Value const& Value::operator[](std::string const& name) const { + Value const* ptr = get(name); + return (ptr ? *ptr : Defaults::value_); +} +Value const& Value::operator[](char const* name) const { + Value const* ptr = get(name); + return (ptr ? *ptr : Defaults::value_); +} +Value& Value::operator[](std::string const& name) { + setType(tObject); + return (*map_)[name]; +} +Value& Value::operator[](char const* name) { + setType(tObject); + return (*map_)[name]; +} + +// tArray +Value::Array const& Value::getArray() const { + return (type_ == tArray ? *array_ : Defaults::value_); +} +uint32 Value::length() const { + return (type_ == tArray ? array_->size() : 0); +} +Value const* Value::at(uint32 i) const { + if (type_ != tArray || i >= array_->size()) return nullptr; + return &(*array_)[i]; +} +Value* Value::at(uint32 i) { + if (type_ != tArray || i >= array_->size()) return nullptr; + return &(*array_)[i]; +} +Value& Value::insert(uint32 i, Value const& data) { + setType(tArray); + return *array_->insert(array_->begin() + std::min(i, array_->size()), data); +} +Value& Value::append(Value const& data) { + setType(tArray); + array_->push_back(data); + return array_->back(); +} +void Value::remove(uint32 i) { + if (type_ != tArray || i >= array_->size()) return; + array_->erase(array_->begin() + i); +} +Value const& Value::operator[](int i) const { + if (type_ != tArray || i < 0 || i >= array_->size()) return Defaults::value_; + return (*array_)[i]; +} +Value& Value::operator[](int i) { + setType(tArray); + if (i < 0) i = 0; + if (array_->size() <= i) { + array_->resize(i + 1); + } + return (*array_)[i]; +} + +Value::Iterator Value::begin() { + switch (type_) { + case tObject: + return Iterator(map_->begin()); + case tArray: + return Iterator(array_->begin()); + default: + return Iterator(); + } +} +Value::Iterator Value::end() { + switch (type_) { + case tObject: + return Iterator(map_->end()); + case tArray: + return Iterator(array_->end()); + default: + return Iterator(); + } +} +Value::ConstIterator Value::begin() const { + switch (type_) { + case tObject: + return ConstIterator(map_->begin()); + case tArray: + return ConstIterator(array_->begin()); + default: + return ConstIterator(); + } +} +Value::ConstIterator Value::end() const { + switch (type_) { + case tObject: + return ConstIterator(map_->end()); + case tArray: + return ConstIterator(array_->end()); + default: + return ConstIterator(); + } +} + +class Tokenizer { + File* file; + bool strict; +public: + uint32 line; + uint32 col; + int chr; + int move() { + int old = chr; + chr = file->getc(); + if (chr == '\n') ++line; + if (chr == '\r' || chr == '\n') col = 0; + else ++col; + return old; + } + + enum State {tEnd, tSymbol, tInteger, tNumber, tString, tIdentifier, tError = -1}; + State state; + + int valInteger; + double valNumber; + std::string value; + + Tokenizer(File* file, bool strict) + : file(file) + , chr(file->getc()) + , strict(strict) + , line(0) + , col(0) + {} + + State next(); +}; + +Tokenizer::State Tokenizer::next() { + while (chr != EOF && isspace(chr)) { + move(); + } + if (chr == EOF) { + return state = tEnd; + } + value.clear(); + if (chr == '"' || (!strict && chr == '\'')) { + char init = chr; + state = tString; + move(); + while (chr != init && chr != EOF) { + if (chr == '\\') { + move(); + switch (chr) { + case '\'': + case '"': + case '\\': + case '/': + value.push_back(move()); + break; + case 'b': + value.push_back('\b'); + move(); + break; + case 'f': + value.push_back('\f'); + move(); + break; + case 'n': + value.push_back('\n'); + move(); + break; + case 'r': + value.push_back('\r'); + move(); + break; + case 't': + value.push_back('\t'); + move(); + break; + case 'u': { + move(); + uint32 cp = 0; + for (int i = 0; i < 4; ++i) { + if (chr >= '0' && chr <= '9') { + cp = cp * 16 + (move() - '0'); + } else if (chr >= 'a' && chr <= 'f') { + cp = cp * 16 + (move() - 'a') + 10; + } else if (chr >= 'A' && chr <= 'F') { + cp = cp * 16 + (move() - 'A') + 10; + } else { + value = "invalid hex digit"; + return state = tError; + } + } + if (cp <= 0x7F) { + value.push_back(cp); + } else if (cp <= 0x7FF) { + value.push_back(0xC0 | ((cp >> 6) & 0x1F)); + value.push_back(0x80 | (cp & 0x3F)); + } else { + value.push_back(0xE0 | ((cp >> 12) & 0x0F)); + value.push_back(0x80 | ((cp >> 6) & 0x3F)); + value.push_back(0x80 | (cp & 0x3F)); + } + break; + } + default: + value = "invalid escape sequence"; + return state = tError; + } + } else { + value.push_back(move()); + } + } + move(); + } else if (chr == '-' || (chr >= '0' && chr <= '9') || (!strict && (chr == '.' || chr == '+'))) { + state = tInteger; + if (chr == '-' || (!strict && chr == '+')) { + value.push_back(move()); + } + if (chr == '0') { + value.push_back(move()); + } else if (chr >= '1' && chr <= '9') { + while (chr >= '0' && chr <= '9') { + value.push_back(move()); + } + } else if (strict || chr != '.') { + value = "invalid number"; + return state = tError; + } + if (chr == '.') { + state = tNumber; + value.push_back(move()); + if ((chr < '0' || chr > '9') && strict) { + value = "invalid number"; + return state = tError; + } + while (chr >= '0' && chr <= '9') { + value.push_back(move()); + } + } + if (chr == 'e' || chr == 'E') { + state = tNumber; + value.push_back(move()); + if (chr == '-' || chr == '+') { + value.push_back(move()); + } + if (chr < '0' || chr > '9') { + value = "invalid number"; + return state = tError; + } + while (chr >= '0' && chr <= '9') { + value.push_back(move()); + } + } + valNumber = atof(value.c_str()); + if (state == tInteger) { + valInteger = int(valNumber); + if (double(valInteger) != valNumber) { + state = tNumber; + } + } + } else if (chr == '{' || chr == '}' || chr == '[' || chr == ']' || chr == ':' || chr == ',' || chr == '(' || chr == ')') { + state = tSymbol; + value.push_back(move()); + } else if ((chr >= 'a' && chr <= 'z') || (chr >= 'A' && chr <= 'Z') || chr == '_') { + state = tIdentifier; + while ((chr >= 'a' && chr <= 'z') || (chr >= 'A' && chr <= 'Z') || (chr >= '0' && chr <= '9') || chr == '_') { + value.push_back(move()); + } + } else if (chr == '/') { + move(); + if (chr == '/') { + while (chr != '\n' && chr != EOF) { + move(); + } + return next(); + } else if (chr == '*') { + move(); + bool star = false; + while (chr != EOF && (!star || chr != '/')) { + star = (chr == '*'); + move(); + } + if (chr == EOF) { + value = "unterminated comment"; + return state = tError; + } + move(); + return next(); + } else { + value = "unexpected symbol '/'"; + return state = tError; + } + } else { + value = "unexpected symbol '"; + value.push_back(chr); + value.push_back('\''); + return state = tError; + } + return state; +} + +bool parse(File& file, Visitor* visitor, int mode, std::string* func) { + enum State{sValue, sKey, sColon, sNext, sEnd} state = sValue; + std::vector objStack; + bool topEmpty = true; + Tokenizer tok(&file, mode == mJSON); + if (mode == mJSCall) { + if (func) func->clear(); + while (tok.chr != EOF && tok.chr != '(') { + if (func) func->push_back(tok.chr); + tok.move(); + } + if (tok.chr != '(') { + visitor->onError(tok.line, tok.col, "expected '('"); + return false; + } + tok.move(); + } + tok.next(); + while (state != sEnd) { + if (tok.state == Tokenizer::tError) { + visitor->onError(tok.line, tok.col, tok.value); + return false; + } + bool advance = true; + switch (state) { + case sValue: + if (tok.state == Tokenizer::tInteger) { + if (!visitor->onInteger(tok.valInteger)) return false; + } else if (tok.state == Tokenizer::tNumber) { + if (!visitor->onNumber(tok.valNumber)) return false; + } else if (tok.state == Tokenizer::tString) { + if (!visitor->onString(tok.value)) return false; + } else if (tok.state == Tokenizer::tIdentifier) { + if (tok.value == "null") { + if (!visitor->onNull()) return false; + } else if (tok.value == "true") { + if (!visitor->onBoolean(true)) return false; + } else if (tok.value == "false") { + if (!visitor->onBoolean(false)) return false; + } else { + visitor->onError(tok.line, tok.col, "unexpected identifier " + tok.value); + return false; + } + } else if (tok.state == Tokenizer::tSymbol) { + if (tok.value == "{") { + if (!visitor->onOpenMap()) return false; + objStack.push_back(Value::tObject); + topEmpty = true; + state = sKey; + break; + } else if (tok.value == "[") { + if (!visitor->onOpenArray()) return false; + objStack.push_back(Value::tArray); + topEmpty = true; + state = sValue; + break; + } else if ((mode != mJSON || topEmpty) && tok.value == "]" && !objStack.empty() && objStack.back() == Value::tArray) { + state = sNext; + advance = false; + break; + } else { + visitor->onError(tok.line, tok.col, "unexpected symbol '" + tok.value + "'"); + return false; + } + } else { + visitor->onError(tok.line, tok.col, "value expected"); + return false; + } + topEmpty = false; + if (objStack.empty()) { + state = sEnd; + } else { + state = sNext; + } + break; + case sKey: + if (tok.state == Tokenizer::tString) { + if (!visitor->onMapKey(tok.value)) return false; + state = sColon; + } else if (mode != mJSON && tok.state == Tokenizer::tIdentifier) { + if (!visitor->onMapKey(tok.value)) return false; + state = sColon; + } else if (mode != mJSON && (tok.state == Tokenizer::tNumber || tok.state == Tokenizer::tInteger)) { + if (!visitor->onMapKey(tok.value)) return false; + state = sColon; + } else if ((mode != mJSON || topEmpty) && tok.state == Tokenizer::tSymbol && tok.value == "}") { + state = sNext; + advance = false; + } else { + visitor->onError(tok.line, tok.col, "object key expected"); + return false; + } + break; + case sColon: + if (tok.state == Tokenizer::tSymbol && tok.value == ":") { + state = sValue; + } else { + visitor->onError(tok.line, tok.col, "':' expected"); + return false; + } + break; + case sNext: + if (tok.state == Tokenizer::tSymbol) { + if (tok.value == ",") { + if (objStack.back() == Value::tObject) { + state = sKey; + } else { + state = sValue; + } + } else if (tok.value == "}") { + if (objStack.back() != Value::tObject) { + visitor->onError(tok.line, tok.col, "mismatched '}'"); + return false; + } + if (!visitor->onCloseMap()) return false; + } else if (tok.value == "]") { + if (objStack.back() != Value::tArray) { + visitor->onError(tok.line, tok.col, "mismatched ']'"); + return false; + } + if (!visitor->onCloseArray()) return false; + } else { + visitor->onError(tok.line, tok.col, "unexpected symbol '" + tok.value + "'"); + return false; + } + if (state == sNext) { + objStack.pop_back(); + topEmpty = false; + if (objStack.empty()) { + advance = false; + state = sEnd; + } else { + state = sNext; + } + } + } else { + if (objStack.back() == Value::tObject) { + visitor->onError(tok.line, tok.col, "'}' or ',' expected"); + } else { + visitor->onError(tok.line, tok.col, "']' or ',' expected"); + } + return false; + } + break; + default: + visitor->onError(tok.line, tok.col, "internal error"); + return false; + } + if (advance) { + tok.next(); + } + } + if (mode == mJSCall) { + if (tok.next() != Tokenizer::tSymbol || tok.value != ")") { + visitor->onError(tok.line, tok.col, "expected ')'"); + return false; + } + tok.move(); + if (tok.chr == ';') tok.move(); + //while (tok.chr != EOF && isspace(tok.chr)) tok.move(); + //if (tok.chr != EOF) { + // visitor->onError(tok.line, tok.col, fmtstring("unexpected symbol '%c'", (char)tok.chr)); + // return false; + //} + } else { + //if (tok.next() != Tokenizer::tEnd) { + // visitor->onError(tok.line, tok.col, fmtstring("unexpected symbol '%c'", (char)tok.chr)); + // return false; + //} + } + file.seek(-1, SEEK_CUR); + return visitor->onEnd(); +} + +BuilderVisitor::BuilderVisitor(Value& value, bool throwExceptions) + : Visitor(throwExceptions) + , value_(value) + , state_(sStart) +{} + +bool BuilderVisitor::openComplexValue(Value::Type type) { + switch (state_) { + case sStart: + stack_.push_back(&value_.setType(type)); + break; + case sArrayValue: + stack_.push_back(&stack_.back()->append(type)); + break; + case sMapValue: + stack_.push_back(&stack_.back()->insert(key_, type)); + state_ = sMapKey; + break; + default: + return false; + } + return true; +} + +bool BuilderVisitor::closeComplexValue() { + stack_.pop_back(); + if (!stack_.empty()) { + switch (stack_.back()->type()) { + case Value::tObject: + state_ = sMapKey; + break; + case Value::tArray: + state_ = sArrayValue; + break; + default: + return false; + } + } else { + state_ = sFinish; + } + return true; +} + +bool parse(File& file, Value& value, int mode, std::string* func, bool throwExceptions) { + BuilderVisitor builder(value, throwExceptions); + value.clear(); + return parse(file, &builder, mode, func); +} + +bool Value::walk(Visitor* visitor) const { + switch (type_) { + case tUndefined: + case tNull: + return visitor->onNull(); + case tBoolean: + return visitor->onBoolean(bool_); + case tString: + return visitor->onString(*string_); + case tInteger: + return visitor->onInteger(int_); + case tNumber: + return visitor->onNumber(number_); + case tObject: + if (!visitor->onOpenMap()) return false; + for (auto it = map_->begin(); it != map_->end(); ++it) { + if (!visitor->onMapKey(it->first)) return false; + if (!it->second.walk(visitor)) return false; + } + return visitor->onCloseMap(); + case tArray: + if (!visitor->onOpenArray()) return false; + for (auto it = array_->begin(); it != array_->end(); ++it) { + if (!it->walk(visitor)) return false; + } + return visitor->onCloseArray(); + default: + return false; + } +} + +WriterVisitor::WriterVisitor(File& file, int mode, char const* func) + : file_(file) + , mode_(mode) + , escape_(false) + , empty_(true) + , object_(false) +{ + if (mode == mJSCall) { + file.printf("%s(", func ? func : ""); + } +} + +void WriterVisitor::onValue() { + if (!object_ && !empty_) { + file_.putc(','); + } + if (!object_ && !curIndent_.empty()) { + file_.printf("\n%s", curIndent_.c_str()); + } + empty_ = false; + object_ = false; +} + +void WriterVisitor::openValue(char chr) { + onValue(); + empty_ = true; + file_.putc(chr); + curIndent_.append(indent_); +} +void WriterVisitor::closeValue(char chr) { + curIndent_.resize(curIndent_.size() - indent_.size()); + if (!empty_ && !indent_.empty()) { + if (mode_ != mJSON) file_.putc(','); + file_.printf("\n%s", curIndent_.c_str()); + } + file_.putc(chr); + empty_ = false; +} + +void WriterVisitor::writeString(std::string const& str) { + file_.putc('"'); + for (uint32 pos = 0; pos < str.length(); ++pos) { + uint8 chr = static_cast(str[pos]); + switch (chr) { + case '"': + file_.printf("\\\""); + break; + case '\\': + file_.printf("\\\\"); + break; + case '\b': + file_.printf("\\b"); + break; + case '\f': + file_.printf("\\f"); + break; + case '\n': + file_.printf("\\n"); + break; + case '\r': + file_.printf("\\r"); + break; + case '\t': + file_.printf("\\t"); + break; + default: + if (chr < 32) { + file_.printf("\\u%04X", (int)chr); + } else if (chr <= 0x7F || !escape_) { + file_.putc(chr); + } else { + uint8 hdr = chr; + uint32 mask = 0x3F; + uint32 cp = chr; + while ((hdr & 0xC0) == 0xC0 && pos < str.length() - 1) { + chr = str[++pos]; + cp = (cp << 6) | (chr & 0x3F); + mask = (mask << 5) | 0x1F; + hdr <<= 1; + } + cp &= mask; + file_.printf("\\u%04X", cp & 0xFFFF); + } + } + } + file_.putc('"'); +} + +bool WriterVisitor::onNull() { + onValue(); + file_.printf("null"); + return true; +} +bool WriterVisitor::onBoolean(bool val) { + onValue(); + file_.printf(val ? "true" : "false"); + return true; +} +bool WriterVisitor::onInteger(int val) { + onValue(); + file_.printf("%d", val); + return true; +} +bool WriterVisitor::onNumber(double val) { + onValue(); + file_.printf("%.14g", val); + return true; +} +bool WriterVisitor::onString(std::string const& val) { + onValue(); + writeString(val); + return true; +} +bool WriterVisitor::onMapKey(std::string const& key) { + onValue(); + object_ = true; + bool safe = (mode_ != mJSON) && + (!key.empty() && ((key[0] >= 'a' && key[0] <= 'z') + || (key[0] >= 'A' && key[0] <= 'Z') + || key[0] == '_')); + for (uint32 pos = 1; pos < key.length() && safe; ++pos) { + if ((key[pos] < 'a' || key[pos] > 'z') && + (key[pos] < 'A' || key[pos] > 'Z') && + (key[pos] < '0' || key[pos] > '9') && key[pos] != '_') { + safe = false; + } + } + if (safe) { + file_.printf("%s", key.c_str()); + } else { + writeString(key); + } + file_.putc(':'); + if (!indent_.empty()) file_.putc(' '); + return true; +} +bool WriterVisitor::onEnd() { + if (mode_ == mJSCall) file_.printf(");"); + if (!indent_.empty()) file_.putc('\n'); + return true; +} + +bool write(File& file, Value const& value, int mode, char const* func) { + WriterVisitor writer(file, mode, func); + writer.setIndent(2); + if (!value.walk(&writer)) return false; + return writer.onEnd(); +} + +bool Visitor::printExStrings = true; + +} diff --git a/src/base/json.h b/src/base/json.h new file mode 100644 index 0000000..74f94ac --- /dev/null +++ b/src/base/json.h @@ -0,0 +1,381 @@ +#pragma once + +#include "types.h" +#include "file.h" +#include "error.h" +#include +#include + +namespace json { + +class Visitor; + +class Value { +public: + typedef std::map Map; + typedef std::vector Array; + enum Type { tUndefined, tNull, tString, tInteger, tNumber, tObject, tArray, tBoolean }; + +private: + Type type_; + union { + int int_; + double number_; + std::string* string_; + Map* map_; + Array* array_; + bool bool_; + }; +public: + typedef std::map Map; + typedef std::vector Array; + + Value(Type type = tUndefined); + ~Value() { + clear(); + } + + Value(bool val); + Value(int val); + Value(unsigned int val); + Value(sint32 val); + Value(uint32 val); + Value(sint64 val); + Value(uint64 val); + Value(double val); + Value(std::string const& val); + Value(char const* val); + Value(Value const& val); + + Value& operator=(Value const& rhs); + Value& setValue(Value const& rhs) { + return *this = rhs; + } + + void clear(); + Type type() const { + return type_; + } + Value& setType(Type type); + + // tBoolean + bool getBoolean() const; + Value& setBoolean(bool data); + + // tString + std::string const& getString() const; + Value& setString(std::string const& data); + Value& setString(char const* data); + + // tNumber + bool isInteger() const; + int getInteger() const; + double getNumber() const; + Value& setInteger(int data); + Value& setNumber(double data); + + // tObject + Map const& getMap() const; + bool has(std::string const& name) const; + bool has(char const* name) const; + Value const* get(std::string const& name) const; + Value* get(std::string const& name); + Value const* get(char const* name) const; + Value* get(char const* name); + Value& insert(std::string const& name, Value const& data); + void remove(std::string const& name); + Value& insert(char const* name, Value const& data); + void remove(char const* name); + Value const& operator[](std::string const& name) const; + Value& operator[](std::string const& name); + Value const& operator[](char const* name) const; + Value& operator[](char const* name); + + bool hasProperty(char const* name, uint8 type) const { + Value const* prop = get(name); + return prop && prop->type() == type; + } + + // tArray + Array const& getArray() const; + uint32 length() const; + Value const* at(uint32 i) const; + Value* at(uint32 i); + Value& insert(uint32 i, Value const& data); + Value& append(Value const& data); + void remove(uint32 i); + Value const& operator[](int i) const; + Value& operator[](int i); + + class Iterator { + Type type_; + Map::iterator map_; + Array::iterator array_; + friend class Value; + Iterator(Map::iterator map) : map_(map), type_(tObject) {} + Iterator(Array::iterator array) : array_(array), type_(tArray) {} + public: + Iterator() : type_(tUndefined) {} + + Iterator& operator=(Iterator const& it) { + type_ = it.type_; + if (type_ == tObject) map_ = it.map_; + if (type_ == tArray) array_ = it.array_; + return *this; + } + Iterator& operator++() { + if (type_ == tObject) ++map_; + if (type_ == tArray) ++array_; + return *this; + } + bool operator==(Iterator const& it) { + if (type_ != it.type_) return false; + if (type_ == tObject) return map_ == it.map_; + if (type_ == tArray) return array_ == it.array_; + return true; + } + bool operator!=(Iterator const& it) { + if (type_ != it.type_) return true; + if (type_ == tObject) return map_ != it.map_; + if (type_ == tArray) return array_ != it.array_; + return false; + } + + Value& operator*() const { + if (type_ == tObject) return map_->second; + return *array_; + } + Value* operator->() const { + if (type_ == tObject) return &map_->second; + return &*array_; + } + std::string const& key() const { + return map_->first; + } + }; + + class ConstIterator { + Type type_; + Map::const_iterator map_; + Array::const_iterator array_; + friend class Value; + ConstIterator(Map::const_iterator map) : map_(map), type_(tObject) {} + ConstIterator(Array::const_iterator array) : array_(array), type_(tArray) {} + public: + ConstIterator() : type_(tUndefined) {} + + ConstIterator& operator=(ConstIterator const& it) { + type_ = it.type_; + if (type_ == tObject) map_ = it.map_; + if (type_ == tArray) array_ = it.array_; + return *this; + } + ConstIterator& operator++() { + if (type_ == tObject) ++map_; + if (type_ == tArray) ++array_; + return *this; + } + bool operator==(ConstIterator const& it) { + if (type_ != it.type_) return false; + if (type_ == tObject) return map_ == it.map_; + if (type_ == tArray) return array_ == it.array_; + return true; + } + bool operator!=(ConstIterator const& it) { + if (type_ != it.type_) return true; + if (type_ == tObject) return map_ != it.map_; + if (type_ == tArray) return array_ != it.array_; + return false; + } + + Value const& operator*() const { + if (type_ == tObject) return map_->second; + return *array_; + } + Value const* operator->() const { + if (type_ == tObject) return &map_->second; + return &*array_; + } + std::string const& key() const { + return map_->first; + } + }; + + Iterator begin(); + Iterator end(); + ConstIterator begin() const; + ConstIterator end() const; + + bool walk(Visitor* visitor) const; +}; + +class Visitor { +public: + explicit Visitor(bool throwExceptions = false) + : throw_(throwExceptions) + {} + + static bool printExStrings; + + virtual ~Visitor() {} + virtual bool onNull() { return true; } + virtual bool onBoolean(bool val) { return true; } + virtual bool onInteger(int val) { return true; } + virtual bool onNumber(double val) { return true; } + virtual bool onString(std::string const& val) { return true; } + virtual bool onOpenMap() { return true; } + virtual bool onMapKey(std::string const& key) { return true; } + virtual bool onCloseMap() { return true; } + virtual bool onOpenArray() { return true; } + virtual bool onCloseArray() { return true; } + virtual bool onIntegerEx(int val, std::string const& alt) { + if (printExStrings) { + return onString(alt); + } else { + return onInteger(val); + } + } + + virtual bool onEnd() { return true; } + virtual void onError(uint32 line, uint32 col, std::string const& reason) { + if (throw_) throw Exception("JSON error at (%d:%d): %s", line + 1, col + 1, reason.c_str()); + } +private: + bool throw_; +}; + +class BuilderVisitor : public Visitor { +public: + BuilderVisitor(Value& value, bool throwExceptions = false); + bool onNull() { + return setValue(Value::tNull); + } + bool onBoolean(bool val) { + return setValue(val); + } + bool onInteger(int val) { + return setValue(val); + } + bool onNumber(double val) { + return setValue(val); + } + bool onString(std::string const& val) { + return setValue(val); + } + bool onOpenMap() { + bool res = openComplexValue(Value::tObject); + if (res) state_ = sMapKey; + return res; + } + bool onMapKey(std::string const& key) { + if (state_ != sMapKey) return false; + key_ = key; + state_ = sMapValue; + return true; + } + bool onCloseMap() { + return closeComplexValue(); + } + bool onOpenArray() { + bool res = openComplexValue(Value::tArray); + if (res) state_ = sArrayValue; + return res; + } + bool onCloseArray() { + return closeComplexValue(); + } +protected: + Value& value_; + std::string key_; + std::vector stack_; + enum { + sStart, + sMapValue, + sMapKey, + sArrayValue, + sFinish, + } state_; + + template + bool setValue(T const& value) { + switch (state_) { + case sStart: + value_.setValue(value); + break; + case sMapValue: + stack_.back()->insert(key_, value); + state_ = sMapKey; + break; + case sArrayValue: + stack_.back()->append(value); + break; + default: + return false; + } + return true; + } + + bool openComplexValue(Value::Type type); + bool closeComplexValue(); +}; + +enum {mJSON, mJS, mJSCall}; + +bool parse(File& file, Visitor* visitor, int mode = mJSON, std::string* func = nullptr); +bool parse(File& file, Value& value, int mode = mJSON, std::string* func = nullptr, bool throwExceptions = false); + +class WriterVisitor : public Visitor { +public: + WriterVisitor(File& file, int mode = mJSON, char const* func = nullptr); + + void setIndent(std::string indent) { + indent_ = indent; + } + void setIndent(int indent) { + indent_.assign(indent, ' '); + } + void escapeUnicode(bool escape) { + escape_ = escape; + } + + bool onNull(); + bool onBoolean(bool val); + bool onInteger(int val); + bool onNumber(double val); + bool onString(std::string const& val); + bool onOpenMap() { + openValue('{'); + return true; + } + bool onMapKey(std::string const& key); + bool onCloseMap() { + closeValue('}'); + return true; + } + bool onOpenArray() { + openValue('['); + return true; + } + bool onCloseArray() { + closeValue(']'); + return true; + } + bool onEnd(); + +protected: + File& file_; + int mode_; + bool escape_; + bool empty_; + bool object_; + std::string indent_; + std::string curIndent_; + void onValue(); + void openValue(char chr); + void closeValue(char chr); + void writeString(std::string const& str); +}; + +bool write(File& file, Value const& value, int mode = mJSON, char const* func = nullptr); + +} diff --git a/src/base/path.cpp b/src/base/path.cpp new file mode 100644 index 0000000..a8f14eb --- /dev/null +++ b/src/base/path.cpp @@ -0,0 +1,91 @@ +#include "path.h" +#include +using namespace std; + +string path::name(string const& path) { + int pos = path.length(); + while (pos > 0 && path[pos - 1] != '/' && path[pos - 1] != '\\') { + --pos; + } + return path.substr(pos); +} +string path::title(string const& path) { + int pos = path.length(); + int dot = path.length(); + while (pos > 0 && path[pos - 1] != '/' && path[pos - 1] != '\\') { + --pos; + if (path[pos] == '.' && dot == path.length()) { + dot = pos; + } + } + if (dot == pos) { + return path.substr(pos); + } else { + return path.substr(pos, dot - pos); + } +} +string path::path(string const& path) { + int pos = path.length(); + while (pos > 0 && path[pos - 1] != '/' && path[pos - 1] != '\\') { + --pos; + } + return path.substr(0, pos ? pos - 1 : 0); +} +string path::ext(string const& path) { + int pos = path.length(); + int dot = path.length(); + while (pos > 0 && path[pos - 1] != '/' && path[pos - 1] != '\\') { + --pos; + if (path[pos] == '.' && dot == path.length()) { + dot = pos; + } + } + if (dot == pos) { + return ""; + } else { + return path.substr(dot); + } +} + +void path::create(std::string const& path) { + std::string buf; + for (char chr : path) { + if (chr == '/' || chr == '\\') chr = sep; + buf.push_back(chr); + if (chr == sep) { + CreateDirectory(buf.c_str(), nullptr); + } + } + if (!buf.empty() && buf.back() != sep) { + CreateDirectory(buf.c_str(), nullptr); + } +} + +#include +std::string path::root() { + static std::string root_; + if (root_.empty()) { + char buffer[512]; + GetModuleFileName(GetModuleHandle(NULL), buffer, sizeof buffer); + root_ = path(buffer); + } + return root_; + //char buffer[512]; + //GetCurrentDirectory(sizeof buffer, buffer); + //return buffer; +} + +string operator / (string const& lhs, string const& rhs) { + if (lhs.empty() || rhs.empty()) return lhs + rhs; + bool left = (lhs.back() == '\\' || lhs.back() == '/'); + bool right = (rhs.front() == '\\' || rhs.front() == '/'); + if (left && right) { + string res = lhs; + res.pop_back(); + return res + rhs; + } else if (!left && !right) { + return lhs + path::sep + rhs; + } else { + return lhs + rhs; + } +} diff --git a/src/base/path.h b/src/base/path.h new file mode 100644 index 0000000..1fa883d --- /dev/null +++ b/src/base/path.h @@ -0,0 +1,17 @@ +#pragma once + +#include + +namespace path { + std::string name(std::string const& path); + std::string title(std::string const& path); + std::string path(std::string const& path); + std::string ext(std::string const& path); + void create(std::string const& path); + + std::string root(); + + static const char sep = '\\'; +} + +std::string operator / (std::string const& lhs, std::string const& rhs); diff --git a/src/base/pool.cpp b/src/base/pool.cpp new file mode 100644 index 0000000..6c2636d --- /dev/null +++ b/src/base/pool.cpp @@ -0,0 +1,100 @@ +#include "pool.h" + +struct FixedMemoryPool::MemoryChunk { + MemoryChunk* next; + MemoryChunk* nextFree; + + uint8* begin; + uint8* end; + uint8* firstFree; + uint8* firstUnused; + uint32 itemSize; + + MemoryChunk(uint32 size, uint32 count); + ~MemoryChunk(); + + bool hasSpace() { + return firstFree || firstUnused < end; + } + bool contains(uint8* ptr) { + return ptr >= begin && ptr < end; + } + uint8* alloc(); + void free(uint8* ptr); +}; + +FixedMemoryPool::MemoryChunk::MemoryChunk(uint32 size, uint32 count) + : next(nullptr) + , nextFree(nullptr) + , itemSize(size) + , firstFree(nullptr) +{ + begin = new uint8[size * count]; + end = begin + size * count; + firstUnused = begin; +} +FixedMemoryPool::MemoryChunk::~MemoryChunk() { + delete[] begin; +} + +uint8* FixedMemoryPool::MemoryChunk::alloc() { + if (firstFree) { + uint8* result = firstFree; + firstFree = *(uint8**)firstFree; + return result; + } else if (firstUnused < end) { + uint8* result = firstUnused; + firstUnused += itemSize; + return result; + } else { + return nullptr; + } +} +void FixedMemoryPool::MemoryChunk::free(uint8* ptr) { + *(uint8**)ptr = firstFree; + firstFree = ptr; +} + +FixedMemoryPool::FixedMemoryPool(uint32 itemSize, uint32 poolGrow) + : itemSize(itemSize) + , chunkSize(poolGrow / itemSize) + , chunks(nullptr) + , freeChunks(nullptr) +{ +} +FixedMemoryPool::~FixedMemoryPool() { + clear(); +} + +void* FixedMemoryPool::alloc() { + if (freeChunks == nullptr) { + freeChunks = new MemoryChunk(itemSize, chunkSize); + freeChunks->next = chunks; + chunks = freeChunks; + } + void* ptr = freeChunks->alloc(); + if (!freeChunks->hasSpace()) { + freeChunks = freeChunks->nextFree; + } + return ptr; +} +void FixedMemoryPool::free(void* ptr) { + for (MemoryChunk* chunk = chunks; chunk; chunk = chunk->next) { + if (chunk->contains((uint8*)ptr)) { + if (!chunk->hasSpace()) { + chunk->nextFree = freeChunks; + freeChunks = chunk; + } + chunk->free((uint8*)ptr); + return; + } + } +} +void FixedMemoryPool::clear() { + while (chunks) { + MemoryChunk* next = chunks->next; + delete chunks; + chunks = next; + } + freeChunks = nullptr; +} diff --git a/src/base/pool.h b/src/base/pool.h new file mode 100644 index 0000000..8c3f2c9 --- /dev/null +++ b/src/base/pool.h @@ -0,0 +1,55 @@ +#pragma once + +#include "types.h" + +class FixedMemoryPool { +public: + FixedMemoryPool(uint32 itemSize, uint32 poolGrow = 65536); + FixedMemoryPool(FixedMemoryPool&& src) + : itemSize(src.itemSize) + , chunkSize(src.chunkSize) + , chunks(src.chunks) + , freeChunks(src.freeChunks) + { + src.chunks = nullptr; + src.freeChunks = nullptr; + } + FixedMemoryPool(FixedMemoryPool const& src) = delete; + ~FixedMemoryPool(); + + void* alloc(); + void free(void* ptr); + + void clear(); + +private: + struct MemoryChunk; + uint32 itemSize; + uint32 chunkSize; + MemoryChunk* chunks; + MemoryChunk* freeChunks; +}; +template +class TypedMemoryPool : private FixedMemoryPool +{ +public: + TypedMemoryPool(uint32 poolGrow = 65536) + : FixedMemoryPool(sizeof(T), poolGrow) + { + } + TypedMemoryPool(TypedMemoryPool&& src) + : FixedMemoryPool(std::move(src)) + { + } + TypedMemoryPool(TypedMemoryPool const& src) = delete; + + T* alloc() { + T* ptr = (T*)FixedMemoryPool::alloc(); + new(ptr)T(); + return ptr; + } + void free(T* ptr) { + ptr->~T(); + FixedMemoryPool::free(ptr); + } +}; diff --git a/src/base/regexp.cpp b/src/base/regexp.cpp new file mode 100644 index 0000000..1c9e41a --- /dev/null +++ b/src/base/regexp.cpp @@ -0,0 +1,837 @@ +#include +#include + +#include "regexp.h" +#include "utf8.h" +#include "path.h" + +namespace re { + +static uint32 unescape(uint8_const_ptr& chr) { + uint32 cp = 0; + switch (*chr) { + case 'a': chr++; return '\a'; + case 'b': chr++; return '\b'; + case 'f': chr++; return '\f'; + case 'n': chr++; return '\n'; + case 'r': chr++; return '\r'; + case 't': chr++; return '\t'; + case 'v': chr++; return '\v'; + case 'u': + chr++; + for (int z = 0; z < 4 && isxdigit(*chr); z++) { + cp *= 16; + if (*chr >= '0' && *chr <= '9') cp += int(*chr - '0'); + else if (*chr >= 'a' && *chr <= 'f') cp += 10 + int(*chr - 'a'); + else if (*chr >= 'A' && *chr <= 'F') cp += 10 + int(*chr - 'A'); + chr++; + } + return cp; + default: + if (isdigit(*chr)) { + for (int z = 0; z < 3 && isdigit(*chr); z++) + cp = cp * 10 + int((*chr++) - '0'); + return cp; + } else { + return utf8::parse(utf8::transform(&chr, NULL)); + } + } +} +static uint32 getchar(uint8_const_ptr& chr, uint32* table = NULL) { + if (*chr == '\\') return unescape(++chr); + return utf8::parse(utf8::transform(&chr, table)); +} + +void CharacterClass::sort() { + std::sort(data.begin(), data.end()); + uint32 shift = 0; + for (uint32 i = 0; i < data.size(); i++) { + if (data[i].end < data[i].begin || (i - shift > 0 && data[i].end <= data[i - shift - 1].end)) { + shift++; + } else if (i - shift > 0 && data[i].begin <= data[i - shift - 1].end + 1) { + data[i - shift - 1].end = data[i].end; + shift++; + } else if (shift) { + data[i - shift] = data[i]; + } + } + data.resize(data.size() - shift); +} + +static void _addchar(std::string& res, char chr) { + if (chr == ']' || chr == '-' || chr == '\\' || chr == '^' || chr == '[') { + res.push_back('\\'); + } + res.push_back(chr); +} + +std::string CharacterClass::format() const { + bool chrmap[256]; + int has = 0, not = 0; + for (uint32 cp = 32; cp <= 126; ++cp) { + chrmap[cp] = match(cp); + has += (chrmap[cp] == true); + not += (chrmap[cp] == false); + } + if (!not) return "."; + std::string res("["); + if (has > not) { + res.push_back('^'); + for (uint32 cp = 32; cp <= 126; ++cp) { + chrmap[cp] = !chrmap[cp]; + } + } + for (uint32 cp = 32; cp <= 126; ++cp) { + if (chrmap[cp]) { + uint32 start = cp; + while (cp < 126 && chrmap[cp + 1]) { + ++cp; + } + if (cp > start + 2) { + _addchar(res, start); + res.push_back('-'); + _addchar(res, cp); + } else { + for (uint32 i = start; i <= cp; ++i) { + _addchar(res, i); + } + } + } + } + res.push_back(']'); + return res; +} + +void CharacterClass::addClass(CharacterClass const& cls) { + funcs.insert(funcs.end(), cls.funcs.begin(), cls.funcs.end()); + if (cls.invert) { + if (cls.data.size() == 0) { + addRange(0, 0xFFFFFFFF); + } else { + if (cls.data[0].begin > 0) { + addRange(0, cls.data[0].begin - 1); + } + for (int i = 1; i < cls.data.size(); i++) { + addRange(cls.data[i - 1].end + 1, cls.data[i].begin - 1); + } + if (cls.data[cls.data.size() - 1].end < 0xFFFFFFFF) { + addRange(cls.data[cls.data.size() - 1].end + 1, 0xFFFFFFFF); + } + } + } else { + data.insert(data.end(), cls.data.begin(), cls.data.end()); + } +} +uint8_const_ptr CharacterClass::init(char const* src, int flags) { + invert = (*src == '^'); + if (invert) src++; + uint8_const_ptr pos = (uint8_const_ptr)src; + uint32* table = ((flags & Prog::CaseInsensitive) ? utf8::tf_lower : NULL); + while (*pos && (pos == (uint8_const_ptr)src || *pos != ']')) { + uint32 cp = -1; + if (*pos == '\\') { + pos++; + CharacterClass* cls = getDefault(*pos, flags); + if (cls) { + pos++; + addClass(*cls); + } else { + cp = unescape(pos); + if (flags & Prog::CaseInsensitive) cp = towlower(cp); + } + } else { + cp = utf8::parse(utf8::transform(&pos, table)); + } + if (cp != -1) { + if (*pos == '-' && pos[1] && pos[1] != ']') { + pos++; + addRange(cp, getchar(pos, table)); + } else { + addRange(cp, cp); + } + } + } + sort(); + return pos; +} +bool CharacterClass::match(uint32 c) const { + int left = 0; + int right = data.size() - 1; + while (left <= right) { + int mid = (left + right) / 2; + if (c >= data[mid].begin && c <= data[mid].end) { + return !invert; + } + if (c < data[mid].begin) { + right = mid - 1; + } else { + left = mid + 1; + } + } + for (int i = 0; i < funcs.size(); i++) { + if (funcs[i](c)) return !invert; + } + return invert; +} + +static CharacterClass uClsNormal[] = { + CharacterClass("a-zA-Z0-9_"), + CharacterClass("^a-zA-Z0-9_"), + CharacterClass("0-9"), + CharacterClass("a-fA-F0-9"), + CharacterClass("^0-9"), + CharacterClass(" \t\n\r\f\v"), + CharacterClass("^ \t\n\r\f\v"), + CharacterClass("^\n"), + CharacterClass("^"), +}; + +static bool u_word(uint32 cp) { + return cp == '_' || (cp >= '0' && cp <= '9') || iswalnum(cp); +} +static bool u_notword(uint32 cp) { + return !(cp == '_' || (cp >= '0' && cp <= '9') || iswalnum(cp)); +} +static bool u_space(uint32 cp) { + return iswspace(cp); +} +static bool u_notspace(uint32 cp) { + return !iswspace(cp); +} +static CharacterClass uClsUnicode[] = { + CharacterClass(u_word), + CharacterClass(u_notword), + CharacterClass(u_space), + CharacterClass(u_notspace), +}; + +CharacterClass* CharacterClass::getDefault(char ch, int flags) { + switch (ch) { + case 'w': return (flags & Prog::Unicode ? &uClsUnicode[0] : &uClsNormal[0]); + case 'W': return (flags & Prog::Unicode ? &uClsUnicode[1] : &uClsNormal[1]); + case 'd': return &uClsNormal[2]; + case 'x': return &uClsNormal[3]; + case 'D': return &uClsNormal[4]; + case 's': return (flags & Prog::Unicode ? &uClsUnicode[2] : &uClsNormal[5]); + case 'S': return (flags & Prog::Unicode ? &uClsUnicode[3] : &uClsNormal[6]); + case '.': return (flags & Prog::DotAll ? &uClsNormal[8] : &uClsNormal[7]); + default: return NULL; + } +} + +///////////////////////////////////// + +struct State { + enum Type { + NONE = 0, + START = 1, + RBRA, + RBRANC, + LBRA, + LBRANC, + OR, + CAT, + STAR, + PLUS, + QUEST, + NOP, + OPERAND, + BOL, + EOL, + CHAR, + CCLASS, + END + }; + Type type; + union { + CharacterClass const* mask; + uint32 chr; + int subid; + State* left; + }; + union { + State* right; + State* next; + }; + int list; +}; +struct Operand { + State* first; + State* last; +}; +struct Operator { + State::Type type; + int subid; +}; +#define MAXSTACK 32 +struct Compiler { + State* states; + int maxStates; + int numStates; + + Operand andstack[MAXSTACK]; + int andsize; + Operator atorstack[MAXSTACK]; + int atorsize; + + bool lastand; + int brackets; + int cursub; + + int optsize; + + void init(char const* expr, int len); + State* operand(State::Type type); + void pushand(State* first, State* last); + void pushator(State::Type type); + void evaluntil(int pri); + int optimize(State* state); + void floatstart(); +}; +void Compiler::init(char const* expr, int length) { + maxStates = length * 6 + 6; + states = new State[maxStates]; + memset(states, 0, sizeof(State)* maxStates); + numStates = 0; + + andsize = 0; + atorstack[0].type = State::NONE; + atorstack[0].subid = 0; + atorsize = 1; + lastand = false; + brackets = 0; + cursub = 0; + + optsize = 0; +} +State* Compiler::operand(State::Type type) { + if (lastand) pushator(State::CAT); + State* s = &states[numStates++]; + s->type = type; + s->mask = NULL; + s->next = NULL; + pushand(s, s); + lastand = true; + return s; +} +void Compiler::pushand(State* first, State* last) { + andstack[andsize].first = first; + andstack[andsize].last = last; + andsize++; +} +void Compiler::pushator(State::Type type) { + if (type == State::RBRA || type == State::RBRANC) { + brackets--; + } + if (type == State::LBRA || type == State::LBRANC) { + if (type == State::LBRA) cursub++; + brackets++; + if (lastand) pushator(State::CAT); + } else { + evaluntil(type); + } + if (type != State::RBRA && type != State::RBRANC) { + atorstack[atorsize].type = type; + atorstack[atorsize].subid = cursub; + atorsize++; + } + lastand = (type == State::STAR || type == State::PLUS || + type == State::QUEST || type == State::RBRA || type == State::RBRANC); +} +void Compiler::evaluntil(int pri) { + State* s1; + State* s2; + while (pri == State::RBRA || pri == State::RBRANC || atorstack[atorsize - 1].type >= pri) { + atorsize--; + switch (atorstack[atorsize].type) { + case State::LBRA: + s1 = &states[numStates++]; + s2 = &states[numStates++]; + s1->type = State::LBRA; + s1->subid = atorstack[atorsize].subid; + s2->type = State::RBRA; + s2->subid = atorstack[atorsize].subid; + + s1->next = andstack[andsize - 1].first; + andstack[andsize - 1].first = s1; + andstack[andsize - 1].last->next = s2; + andstack[andsize - 1].last = s2; + s2->next = NULL; + return; + case State::LBRANC: + return; + case State::OR: + andsize--; + s1 = &states[numStates++]; + s2 = &states[numStates++]; + s1->type = State::OR; + s1->left = andstack[andsize - 1].first; + s1->right = andstack[andsize].first; + s2->type = State::NOP; + s2->mask = NULL; + s2->next = NULL; + andstack[andsize - 1].last->next = s2; + andstack[andsize].last->next = s2; + andstack[andsize - 1].first = s1; + andstack[andsize - 1].last = s2; + break; + case State::CAT: + andsize--; + andstack[andsize - 1].last->next = andstack[andsize].first; + andstack[andsize - 1].last = andstack[andsize].last; + break; + case State::STAR: + s1 = &states[numStates++]; + s1->type = State::OR; + s1->left = andstack[andsize - 1].first; + s1->right = NULL; + andstack[andsize - 1].last->next = s1; + andstack[andsize - 1].first = s1; + andstack[andsize - 1].last = s1; + break; + case State::PLUS: + s1 = &states[numStates++]; + s1->type = State::OR; + s1->left = andstack[andsize - 1].first; + s1->right = NULL; + andstack[andsize - 1].last->next = s1; + andstack[andsize - 1].last = s1; + break; + case State::QUEST: + s1 = &states[numStates++]; + s2 = &states[numStates++]; + s1->type = State::OR; + s1->left = andstack[andsize - 1].first; + s1->right = s2; + s2->type = State::NOP; + s2->mask = NULL; + s2->next = NULL; + andstack[andsize - 1].last->next = s2; + andstack[andsize - 1].first = s1; + andstack[andsize - 1].last = s2; + break; + } + } +} +int Compiler::optimize(State* state) { + if (state->list >= 0) return state->list; + if (state->type == State::NOP) { + state->list = optimize(state->next); + } else { + state->list = optsize++; + if (state->next) + optimize(state->next); + if (state->type == State::OR) + optimize(state->left); + } + return state->list; +} + +struct Thread { + State* state; + Match match; +}; + +Prog::Prog(char const* expr, int length, uint32 f) { + flags = f; + Compiler comp; + if (length < 0) length = strlen(expr); + comp.init(expr, length); + + uint32* ut_table = (flags & CaseInsensitive ? utf8::tf_lower : NULL); + + uint8_const_ptr pos = (uint8_const_ptr)expr; + uint8_const_ptr end = pos + length; + while (pos < end) { + State::Type type = State::CHAR; + CharacterClass const* mask = NULL; + uint32 cp = -1; + switch (*pos) { + case '\\': + pos++; + if (*pos != '.' && (mask = CharacterClass::getDefault(*pos, flags))) { + type = State::CCLASS; + } else { + cp = unescape(pos); + if (flags & CaseInsensitive) cp = towlower(cp); + pos--; + } + break; + case '*': + type = State::STAR; + break; + case '+': + type = State::PLUS; + break; + case '?': + type = State::QUEST; + break; + case '|': + type = State::OR; + break; + case '(': + type = State::LBRA; + break; + case ')': + type = State::RBRA; + break; + case '{': + type = State::LBRANC; + break; + case '}': + type = State::RBRANC; + break; + case '^': + type = State::BOL; + break; + case '$': + type = State::EOL; + break; + case '.': + type = State::CCLASS; + mask = CharacterClass::getDefault('.', flags); + break; + case '[': { + type = State::CCLASS; + CharacterClass* cls = new CharacterClass(); + masks.push_back(cls); + pos = cls->init((char*)pos + 1, flags); + mask = cls; + break; + } + } + if (cp == -1) { + cp = utf8::parse(utf8::transform(&pos, ut_table)); + } else { + pos++; + } + if (type < State::OPERAND) { + comp.pushator(type); + } else { + State* s = comp.operand(type); + if (type == State::CHAR) { + s->chr = cp; + } else if (type == State::CCLASS) { + s->mask = mask; + } + } + } + comp.evaluntil(State::START); + comp.operand(State::END); + comp.evaluntil(State::START); + + for (int i = 0; i < comp.numStates; i++) { + comp.states[i].list = -1; + } + int startpos = comp.optimize(comp.andstack[0].first); + states = new State[comp.optsize]; + for (int i = 0; i < comp.numStates; i++) { + int p = comp.states[i].list; + if (p >= 0 && comp.states[i].type != State::NOP) { + states[p].type = comp.states[i].type; + states[p].next = comp.states[i].next ? &states[comp.states[i].next->list] : NULL; + if (states[p].type == State::CHAR) { + states[p].chr = comp.states[i].chr; + } else if (states[p].type == State::CCLASS) { + states[p].mask = comp.states[i].mask; + } else if (states[p].type == State::OR) { + states[p].left = comp.states[i].left ? &states[comp.states[i].left->list] : NULL; + } else { + states[p].subid = comp.states[i].subid; + } + } + } + delete[] comp.states; + numStates = comp.optsize; + start = &states[startpos]; + + numCaptures = comp.cursub; + maxThreads = 32; + threads = new Thread[maxThreads * 2]; +} +Prog::~Prog() { + delete[] states; + for (int i = 0; i < masks.size(); i++) { + delete masks[i]; + } + delete[] threads; +} +void Prog::addthread(State* state, Match const& match) { + if (state->list < 0) { + if (numThreads[1 - cur] >= maxThreads) { + Thread* newthreads = new Thread[maxThreads * 4]; + memcpy(newthreads, threads, sizeof(Thread)* numThreads[0]); + memcpy(newthreads + maxThreads * 2, threads + maxThreads, sizeof(Thread)* numThreads[1]); + delete[] threads; + threads = newthreads; + maxThreads *= 2; + } + Thread* thread = &threads[(1 - cur) * maxThreads + numThreads[1 - cur]]; + state->list = numThreads[1 - cur]; + numThreads[1 - cur]++; + thread->state = state; + memcpy(&thread->match, &match, sizeof match); + } +} +void Prog::advance(State* state, Match const& match, uint32 cp, char const* ref) { + if (state->type == State::OR) { + advance(state->left, match, cp, ref); + advance(state->right, match, cp, ref); + } else if (state->type == State::LBRA) { + Match m2; + memcpy(&m2, &match, sizeof m2); + m2.start[state->subid] = ref; + advance(state->next, m2, cp, ref); + } else if (state->type == State::RBRA) { + Match m2; + memcpy(&m2, &match, sizeof m2); + m2.end[state->subid] = ref; + advance(state->next, m2, cp, ref); + } else if (state->type == State::BOL) { + if (flags & MultiLine) { + if (ref == matchText || ref[-1] == '\n') { + advance(state->next, match, 0xFFFFFFFF, ref); + } + } else { + if (ref == matchText) { + advance(state->next, match, 0xFFFFFFFF, ref); + } + } + } else if (state->type == State::EOL) { + if (flags & MultiLine) { + if (*ref == 0 || *ref == '\r' || *ref == '\n') { + advance(state->next, match, 0xFFFFFFFF, ref); + } + } else { + if (*ref == 0) { + advance(state->next, match, 0xFFFFFFFF, ref); + } + } + } else { + if (cp == 0xFFFFFFFF) { + addthread(state, match); + } + else if (cp && ((state->type == State::CHAR && cp == state->chr) || + (state->type == State::CCLASS && state->mask->match(cp)))) { + advance(state->next, match, 0xFFFFFFFF, (char*)utf8::next((uint8_const_ptr)ref)); + } + } +} +int Prog::run(char const* text, int length, bool exact, + bool(*callback) (Match const& match, void* arg), void* arg) { + cur = 0; + numThreads[0] = 0; + numThreads[1] = 0; + int pos = 0; + if (length < 0) length = strlen(text); + matchText = text; + int count = 0; + uint32* ut_table = (flags & CaseInsensitive ? utf8::tf_lower : NULL); + + while (true) { + for (int i = 0; i < numStates; i++) { + if (pos > 0 && states[i].type == State::END && states[i].list >= 0 && + (!exact || pos == length)) { + Thread* thread = &threads[cur * maxThreads + states[i].list]; + thread->match.end[0] = text + pos; + count++; + if (callback) { + if (!callback(thread->match, arg)) return count; + } + } + states[i].list = -1; + } + numThreads[1 - cur] = 0; + uint8_const_ptr next = (uint8_const_ptr)(text + pos); + uint32 cp = utf8::parse(utf8::transform(&next, ut_table)); + if (cp == '\r' && *next == '\n') next++; + for (int i = 0; i < numThreads[cur]; i++) { + advance(threads[cur * maxThreads + i].state, threads[cur * maxThreads + i].match, cp, text + pos); + } + if (pos == 0 || !exact) { + Match match; + memset(&match, 0, sizeof match); + match.start[0] = text + pos; + advance(start, match, cp, text + pos); + } + cur = 1 - cur; + if (pos >= length) break; + pos = (char*)next - text; + } + for (int i = 0; i < numStates; i++) { + if (states[i].type == State::END && states[i].list >= 0) { + Thread* thread = &threads[cur * maxThreads + states[i].list]; + thread->match.end[0] = text + pos; + count++; + if (callback) { + if (!callback(thread->match, arg)) return count; + } + } + } + return count; +} + +static bool matcher(Match const& match, void* arg) { + if (arg) { + auto sub = static_cast*>(arg); + sub->clear(); + for (int i = 0; i < sizeof(match.start) / sizeof(match.start[0]) && match.start[i]; i++) { + sub->emplace_back(match.start[i], match.end[i] - match.start[i]); + } + } + return true; +} +bool Prog::match(char const* text, std::vector* sub) { + int res = run(text, -1, true, matcher, sub); + if (res) { + if (sub) { + while (sub->size() <= numCaptures) { + sub->emplace_back(""); + } + } + return true; + } else { + return false; + } +} +static bool finder(Match const& match, void* arg) { + memcpy(arg, &match, sizeof match); + return false; +} +int Prog::find(char const* text, int start, std::vector* sub) +{ + Match match; + if (run(text + start, -1, false, finder, &match)) { + if (sub) { + sub->clear(); + for (int i = 0; i < sizeof(match.start) / sizeof(match.start[0]) && match.start[i]; i++) { + sub->emplace_back(match.start[i], match.end[i] - match.start[i]); + } + while (sub->size() <= numCaptures) { + sub->emplace_back(""); + } + } + return match.start[0] - text; + } else { + return -1; + } +} + +bool operator < (Match const& lhs, Match const& rhs) { + if (lhs.start[0] != rhs.start[0]) return lhs.start[0] < rhs.start[0]; + return lhs.end[0] > rhs.end[0]; +} + +struct FindStruct { + std::vector matches; + std::vector* result = nullptr; + Prog::FindFunc* func = nullptr; + char const* text; + + FindStruct(char const* text) + : text(text) + {} + static bool callback(Match const& match, void* arg); + void finish(); +}; +bool FindStruct::callback(Match const& match, void* arg) { + FindStruct& rs = *(FindStruct*)arg; + rs.matches.push_back(match); + return true; +} +void FindStruct::finish() { + std::sort(matches.begin(), matches.end()); + char const* end = text; + for (auto& m : matches) { + if (m.start[0] >= end) { + if (func) { + func->call(m); + } else { + result->emplace_back(m.start[0], m.end[0] - m.start[0]); + } + end = m.end[0]; + } + } +} +std::vector Prog::findAll(char const* text) { + std::vector result; + FindStruct rs(text); + rs.result = &result; + run(text, -1, false, FindStruct::callback, &rs); + rs.finish(); + return result; +} +void Prog::findAll_(char const* text, FindFunc* func) { + FindStruct rs(text); + rs.func = func; + run(text, -1, false, FindStruct::callback, &rs); + rs.finish(); +} + +struct ReplaceStruct { + std::vector matches; + Prog::ReplaceFunc* func = nullptr; + char const* with = nullptr; + char const* text; + + ReplaceStruct(char const* text) + : text(text) + {} + static bool callback(Match const& match, void* arg); + std::string finish(); +}; +static void addreplace(std::string& result, char const* with, Match const& match) { + for (int i = 0; with[i]; i++) { + if (with[i] == '\\') { + i++; + if (with[i] >= '0' && with[i] <= '9') { + int m = int(with[i] - '0'); + if (match.start[m] && match.end[m]) { + result.append(match.start[m], match.end[m] - match.start[m]); + } + } else { + result.push_back(with[i]); + } + } else { + result.push_back(with[i]); + } + } +} +bool ReplaceStruct::callback(Match const& match, void* arg) { + ReplaceStruct& rs = *(ReplaceStruct*)arg; + rs.matches.push_back(match); + return true; +} +std::string ReplaceStruct::finish() { + std::string result; + std::sort(matches.begin(), matches.end()); + char const* end = text; + for (auto& m : matches) { + if (m.start[0] >= end) { + result.append(end, m.start[0] - end); + if (func) { + result.append(func->call(m)); + } else { + addreplace(result, with, m); + } + end = m.end[0]; + } + } + result.append(end); + return result; +} + +std::string Prog::replace(char const* text, char const* with) { + ReplaceStruct rs(text); + rs.with = with; + run(text, -1, false, ReplaceStruct::callback, &rs); + return rs.finish(); +} +std::string Prog::replace_(char const* text, ReplaceFunc* with) { + ReplaceStruct rs(text); + rs.func = with; + run(text, -1, false, ReplaceStruct::callback, &rs); + return rs.finish(); +} + +} diff --git a/src/base/regexp.h b/src/base/regexp.h new file mode 100644 index 0000000..1b46d87 --- /dev/null +++ b/src/base/regexp.h @@ -0,0 +1,163 @@ +#pragma once + +#include "types.h" +#include +#include + +namespace re { + +typedef bool(*CharTraitFunc)(uint32); +class CharacterClass { + bool invert; + std::vector funcs; + struct Range { + uint32 begin; + uint32 end; + Range(uint32 a = 0, uint32 b = 0) + : begin(a), end(b) + {} + }; + friend bool operator < (Range const& lhs, Range const& rhs) { + return lhs.begin < rhs.begin; + } + std::vector data; + void addRange(uint32 a, uint32 b) { + data.emplace_back(a, b); + } + void addClass(CharacterClass const& cls); + void sort(); +public: + CharacterClass() + : invert(false) + {} + CharacterClass(CharTraitFunc func, bool inv = false) + : invert(inv) + { + funcs.push_back(func); + } + CharacterClass(char const* src, int flags = 0) { + init(src, flags); + } + + uint8_const_ptr init(char const* src, int flags = 0); + + std::string format() const; + + bool match(uint32 c) const; + + static CharacterClass* getDefault(char ch, int flags = 0); +}; + +struct Match { + char const* start[32]; + char const* end[32]; + std::string group(int index) const { + return std::string(start[index], end[index] - start[index]); + } +}; +struct Thread; +struct State; + +class Prog { + uint32 flags; + State* start; + State* states; + int numStates; + std::vector masks; + int numCaptures; + + int maxThreads; + Thread* threads; + int cur; + int numThreads[2]; + char const* matchText; + void addthread(State* state, Match const& match); + void advance(State* state, Match const& match, uint32 cp, char const* ref); + + friend struct FindStruct; + struct FindFunc { + virtual void call(Match const& match) = 0; + }; + template + class FindFuncHolder : public FindFunc { + public: + FindFuncHolder(Func const& func) : func_(func) {} + void call(Match const& match) { func_(match); } + private: + Func const& func_; + }; + + friend struct ReplaceStruct; + struct ReplaceFunc { + virtual std::string call(Match const& match) = 0; + }; + template + class ReplaceFuncHolder : public ReplaceFunc { + public: + ReplaceFuncHolder(Func const& func) : func_(func) {} + std::string call(Match const& match) { return func_(match); } + private: + Func const& func_; + }; + + void findAll_(char const* text, FindFunc* func); + std::string replace_(char const* text, ReplaceFunc* func); +public: + Prog(char const* expr, int length = -1, uint32 flags = 0); + Prog(std::string const& expr, int length = -1, uint32 flags = 0) + : Prog(expr.c_str(), length, flags) + {} + ~Prog(); + + enum { + CaseInsensitive = 0x01, + DotAll = 0x02, + MultiLine = 0x04, + Unicode = 0x08, + }; + + bool match(char const* text, std::vector* sub = NULL); + int find(char const* text, int start = 0, std::vector* sub = NULL); + + bool match(std::string const& text, std::vector* sub = NULL) { + return match(text.c_str(), sub); + } + int find(std::string const& text, int start = 0, std::vector* sub = NULL) { + return find(text.c_str(), start, sub); + } + + std::vector findAll(char const* text); + template + void findAll(char const* text, Func const& func) { + findAll_(text, &FindFuncHolder(func)); + } + + std::vector findAll(std::string const& text) { + return findAll(text.c_str()); + } + template + void findAll(std::string const& text, Func const& func) { + findAll_(text.c_str(), &FindFuncHolder(func)); + } + + template + std::string replace(char const* text, Func const& func) { + return replace_(text, &ReplaceFuncHolder(func)); + } + std::string replace(char const* text, char const* with); + + template + std::string replace(std::string const& text, Func const& func) { + return replace_(text.c_str(), &ReplaceFuncHolder(func)); + } + std::string replace(std::string const& text, char const* with) { + return replace(text.c_str(), with); + } + + int captures() const { + return numCaptures; + } + int run(char const* text, int length, bool exact, bool(*callback) (Match const& match, void* arg), void* arg); +}; + +} diff --git a/src/base/string.cpp b/src/base/string.cpp new file mode 100644 index 0000000..3dc1c4b --- /dev/null +++ b/src/base/string.cpp @@ -0,0 +1,165 @@ +#include "string.h" + +#include +#include +#include +#include "types.h" +#include "error.h" + +std::string strlower(std::string const& str) { + std::string dest(str.size(), ' '); + std::transform(str.begin(), str.end(), dest.begin(), std::tolower); + return dest; +} + +std::vector split(std::string const& str, char sep) { + std::vector res; + std::string cur; + for (char c : str) { + if (c == sep) { + res.push_back(cur); + cur.clear(); + } else { + cur.push_back(c); + } + } + res.push_back(cur); + return res; +} + +std::vector split_multiple(std::string const& str, char const* sep) { + std::vector res; + std::string cur; + for (char c : str) { + if (strchr(sep, c)) { + res.push_back(cur); + cur.clear(); + } else { + cur.push_back(c); + } + } + res.push_back(cur); + return res; +} + +std::vector split(std::string const& str, char const* sep) { + size_t sep_length = strlen(sep); + std::vector res; + size_t pos = 0, next; + while ((next = str.find(sep, pos)) != std::string::npos) { + res.push_back(str.substr(pos, next - pos)); + pos = next + sep_length; + } + res.push_back(str.substr(pos)); + return res; +} + +std::string join(std::vector const& list, char sep) { + std::string res; + for (auto& str : list) { + if (!res.empty()) res.push_back(' '); + res.append(str); + } + return res; +} +std::string join(std::vector const& list, std::string const& sep) { + std::string res; + for (auto& str : list) { + if (!res.empty()) res.append(sep); + res.append(str); + } + return res; +} +std::string fmtstring(char const* fmt, ...) { + va_list ap; + va_start(ap, fmt); + std::string res = varfmtstring(fmt, ap); + va_end(ap); + return res; +} +std::string varfmtstring(char const* fmt, va_list list) { + uint32 len = _vscprintf(fmt, list); + std::string dst; + dst.resize(len + 1); + vsprintf(&dst[0], fmt, list); + dst.resize(len); + return dst; +} + +std::wstring utf8_to_utf16(std::string const& str) { + std::wstring dst; + for (size_t i = 0; i < str.size();) { + uint32 cp = (unsigned char) str[i++]; + size_t next = 0; + if (cp <= 0x7F) { + // do nothing + } else if (cp <= 0xBF) { + throw Exception("not a valid utf-8 string"); + } else if (cp <= 0xDF) { + cp &= 0x1F; + next = 1; + } else if (cp <= 0xEF) { + cp &= 0x0F; + next = 2; + } else if (cp <= 0xF7) { + cp &= 0x07; + next = 3; + } else { + throw Exception("not a valid utf-8 string"); + } + while (next--) { + if (i >= str.size() || str[i] < 0x80 || str[i] > 0xBF) { + throw Exception("not a valid utf-8 string"); + } + cp = (cp << 6) | (str[i++] & 0x3F); + } + if ((cp >= 0xD800 && cp <= 0xDFFF) || cp > 0x10FFFF) { + throw Exception("not a valid utf-8 string"); + } + + if (cp <= 0xFFFF) { + dst.push_back(cp); + } else { + cp -= 0x10000; + dst.push_back((cp >> 10) + 0xD800); + dst.push_back((cp & 0x3FF) + 0xDC00); + } + } + return dst; +} + +std::string utf16_to_utf8(std::wstring const& str) { + std::string dst; + for (size_t i = 0; i < str.size();) { + uint32 cp = str[i++]; + if (cp >= 0xD800 && cp <= 0xDFFF) { + if (cp >= 0xDC00) throw Exception("not a valid utf-16 string"); + if (i >= str.size() || str[i] < 0xDC00 || str[i] > 0xDFFF) throw Exception("not a valid utf-16 string"); + cp = 0x10000 + ((cp - 0xD800) << 10) + (str[i++] - 0xDC00); + } + if (cp >= 0x10FFFF) throw Exception("not a valid utf-16 string"); + if (cp <= 0x7F) { + dst.push_back(cp); + } else if (cp <= 0x7FF) { + dst.push_back((cp >> 6) | 0xC0); + dst.push_back((cp & 0x3F) | 0x80); + } else if (cp <= 0xFFFF) { + dst.push_back((cp >> 12) | 0xE0); + dst.push_back(((cp >> 6) & 0x3F) | 0x80); + dst.push_back((cp & 0x3F) | 0x80); + } else { + dst.push_back((cp >> 18) | 0xF0); + dst.push_back(((cp >> 12) & 0x3F) | 0x80); + dst.push_back(((cp >> 6) & 0x3F) | 0x80); + dst.push_back((cp & 0x3F) | 0x80); + } + } + return dst; +} + +std::string trim(std::string const& str) { + size_t left = 0, right = str.size(); + while (left < str.length() && isspace((unsigned char) str[left])) ++left; + while (right > left && isspace((unsigned char) str[right - 1])) --right; + return str.substr(left, right - left); +} diff --git a/src/base/string.h b/src/base/string.h new file mode 100644 index 0000000..158dfff --- /dev/null +++ b/src/base/string.h @@ -0,0 +1,77 @@ +#pragma once + +#include +#include +#include + +struct ci_char_traits : public std::char_traits < char > { + static bool eq(char c1, char c2) { return std::toupper(c1) == std::toupper(c2); } + static bool ne(char c1, char c2) { return std::toupper(c1) != std::toupper(c2); } + static bool lt(char c1, char c2) { return std::toupper(c1) < std::toupper(c2); } + static int compare(char const* s1, char const* s2, size_t n) { + while (n--) { + char c1 = std::toupper(*s1++); + char c2 = std::toupper(*s2++); + if (c1 != c2) return (c1 < c2 ? -1 : 1); + } + return 0; + } + static char const* find(char const* s, int n, char a) { + a = std::toupper(a); + while (n-- && std::toupper(*s) != a) { + ++s; + } + return (n >= 0 ? s : nullptr); + } +}; + +class istring : public std::basic_string { +public: + typedef std::basic_string _Base; + istring() {} + istring(istring const& str) : _Base(str) {} + istring(std::string const& str) : _Base(str.c_str()) {} + istring(char const* str) : _Base(str) {} + istring(istring&& str) : _Base(str) {} + istring(char const* str, size_t n) : _Base(str) {} + template + istring(Iter begin, Iter end) : _Base(begin, end) {} + + istring& operator=(std::string const& str) { + assign(str.c_str()); + return *this; + } + operator std::string() const { + return std::string(c_str()); + } +}; +std::string strlower(std::string const& src); + +template +inline int basic_compare(T const& lhs, T const& rhs) { + if (lhs < rhs) return -1; + if (lhs > rhs) return 1; + return 0; +} + +std::vector split(std::string const& str, char sep = ' '); +std::vector split(std::string const& str, char const* sep); +std::vector split_multiple(std::string const& str, char const* sep); +std::string join(std::vector const& list, char sep = ' '); +std::string join(std::vector const& list, std::string const& sep); +template +inline std::string join(Iter left, Iter right) { + std::string res; + while (left != right) { + if (!res.empty()) res.push_back(' '); + res.append(*left++); + } + return res; +} + +std::string fmtstring(char const* fmt, ...); +std::string varfmtstring(char const* fmt, va_list list); + +std::wstring utf8_to_utf16(std::string const& str); +std::string utf16_to_utf8(std::wstring const& str); +std::string trim(std::string const& str); diff --git a/src/base/thread.cpp b/src/base/thread.cpp new file mode 100644 index 0000000..3a4c7dd --- /dev/null +++ b/src/base/thread.cpp @@ -0,0 +1,56 @@ +#include "thread.h" +#include + +namespace thread { + + Waitable::~Waitable() { + CloseHandle(handle); + } + bool Waitable::wait(unsigned long timeout) { + return WaitForSingleObject(handle, timeout) == WAIT_OBJECT_0; + } + + Lock::Lock() { + CRITICAL_SECTION* cs = new CRITICAL_SECTION; + InitializeCriticalSection(cs); + impl = cs; + } + Lock::~Lock() { + DeleteCriticalSection((CRITICAL_SECTION*)impl); + } + void Lock::acquire() { + EnterCriticalSection((CRITICAL_SECTION*)impl); + } + void Lock::release() { + LeaveCriticalSection((CRITICAL_SECTION*)impl); + } + + Event::Event(bool initial) + : Waitable(CreateEvent(NULL, TRUE, initial ? TRUE : FALSE, NULL)) + { + } + void Event::set() { + SetEvent(handle); + } + void Event::reset() { + ResetEvent(handle); + } + + Counter::Counter() + : Waitable(CreateEvent(NULL, TRUE, TRUE, NULL)) + , count(0) + { + } + + long Counter::increment() { + long result = InterlockedIncrement(&count); + if (result > 0) ResetEvent(handle); + return result; + } + long Counter::decrement() { + long result = InterlockedDecrement(&count); + if (result <= 0) SetEvent(handle); + return result; + } + +} diff --git a/src/base/thread.h b/src/base/thread.h new file mode 100644 index 0000000..f87b551 --- /dev/null +++ b/src/base/thread.h @@ -0,0 +1,102 @@ +#pragma once + +#include + +namespace thread { + + class Waitable { + protected: + void* handle; + Waitable(void* h) + : handle(h) + { + } + public: + ~Waitable(); + + bool wait(unsigned long timeout = 0xFFFFFFFF); + }; + + class Lock { + void* impl; + public: + Lock(); + ~Lock(); + void acquire(); + void release(); + + class Holder { + Lock* lock; + public: + Holder(Lock* l) + : lock(l) + { + if (lock) lock->acquire(); + } + ~Holder() { + if (lock) lock->release(); + } + }; + }; + class Event : public Waitable { + public: + Event(bool initial = true); + + void set(); + void reset(); + }; + class Counter : public Waitable { + long volatile count; + public: + Counter(); + + operator long() const { + return count; + } + + long increment(); + long decrement(); + }; + + template + struct ThreadArg { + T* ptr; + int (T::*func)(); + }; + template + DWORD WINAPI ThreadProc(LPVOID arg) { + ThreadArg* ta = (ThreadArg*) arg; + int result = (ta->ptr->*(ta->func))(); + delete ta; + return result; + } + template + HANDLE create(T* ptr, int (T::*func)(), unsigned long* id = nullptr) { + ThreadArg* ta = new ThreadArg; + ta->ptr = ptr; + ta->func = func; + return CreateThread(NULL, 0, ThreadProc, ta, 0, id); + } + template + struct ThreadArg1 { + T* ptr; + int (T::*func)(A); + A arg; + }; + template + DWORD WINAPI ThreadProc1(LPVOID arg) { + ThreadArg1* ta = (ThreadArg1*) arg; + int result = (ta->ptr->*(ta->func))(ta->arg); + delete ta; + return result; + } + template + HANDLE create(T* ptr, int (T::*func)(A), A arg, unsigned long* id = nullptr) { + ThreadArg1* ta = new ThreadArg1; + ta->ptr = ptr; + ta->func = func; + ta->arg = arg; + return CreateThread(NULL, 0, ThreadProc1, ta, 0, id); + } + +} diff --git a/src/base/types.h b/src/base/types.h new file mode 100644 index 0000000..cb4a0ee --- /dev/null +++ b/src/base/types.h @@ -0,0 +1,60 @@ +#pragma once + +typedef unsigned char uint8; +typedef signed char sint8; +typedef char int8; +typedef unsigned short uint16; +typedef signed short sint16; +typedef short int16; +typedef unsigned long uint32; +typedef signed long sint32; +typedef long int32; +typedef unsigned __int64 uint64; +typedef signed __int64 sint64; +typedef __int64 int64; + +typedef unsigned char* uint8_ptr; +typedef signed char* sint8_ptr; +typedef char* int8_ptr; +typedef unsigned short* uint16_ptr; +typedef signed short* sint16_ptr; +typedef short* int16_ptr; +typedef unsigned long* uint32_ptr; +typedef signed long* sint32_ptr; +typedef long* int32_ptr; +typedef unsigned __int64* uint64_ptr; +typedef signed __int64* sint64_ptr; +typedef __int64* int64_ptr; + +typedef unsigned char const* uint8_const_ptr; +typedef signed char const* sint8_const_ptr; +typedef char const* int8_const_ptr; +typedef unsigned short const* uint16_const_ptr; +typedef signed short const* sint16_const_ptr; +typedef short const* int16_const_ptr; +typedef unsigned long const* uint32_const_ptr; +typedef signed long const* sint32_const_ptr; +typedef long const* int32_const_ptr; +typedef unsigned __int64 const* uint64_const_ptr; +typedef signed __int64 const* sint64_const_ptr; +typedef __int64 const* int64_const_ptr; + +const uint8 max_uint8 = 0xFFU; +const sint8 min_int8 = sint8(0x80); +const sint8 max_int8 = 0x7F; +const uint16 max_uint16 = 0xFFFFU; +const sint16 min_int16 = sint16(0x8000); +const sint16 max_int16 = 0x7FFF; +const uint32 max_uint32 = 0xFFFFFFFFU; +const sint32 min_int32 = 0x80000000; +const sint32 max_int32 = 0x7FFFFFFF; +const uint64 max_uint64 = 0xFFFFFFFFFFFFFFFFULL; +const sint64 min_int64 = 0x8000000000000000LL; +const sint64 max_int64 = 0x7FFFFFFFFFFFFFFFLL; + +template struct UIntTraits {}; +template<> struct UIntTraits<8> { typedef uint8 Type; }; +template<> struct UIntTraits<16> { typedef uint16 Type; }; +template<> struct UIntTraits<32> { typedef uint32 Type; }; +template<> struct UIntTraits<64> { typedef uint64 Type; }; +template using UInt = typename UIntTraits::Type; diff --git a/src/base/utf8.cpp b/src/base/utf8.cpp new file mode 100644 index 0000000..13a8ee6 --- /dev/null +++ b/src/base/utf8.cpp @@ -0,0 +1,1176 @@ +#include "utf8.h" + +namespace utf8 { + + uint32 UTF8LC3[256] = { + 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, 0, + 0xA0C3, 0xA1C3, 0xA2C3, 0xA3C3, 0xA4C3, 0xA5C3, 0xA6C3, 0xA7C3, 0xA8C3, 0xA9C3, 0xAAC3, 0xABC3, 0xACC3, 0xADC3, 0xAEC3, 0xAFC3, + 0xB0C3, 0xB1C3, 0xB2C3, 0xB3C3, 0xB4C3, 0xB5C3, 0xB6C3, 0, 0xB8C3, 0xB9C3, 0xBAC3, 0xBBC3, 0xBCC3, 0xBDC3, 0xBEC3, 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, + }; + uint32 UTF8LC4[256] = { + 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, 0, + 0x81C4, 0, 0x83C4, 0, 0x85C4, 0, 0x87C4, 0, 0x89C4, 0, 0x8BC4, 0, 0x8DC4, 0, 0x8FC4, 0, + 0x91C4, 0, 0x93C4, 0, 0x95C4, 0, 0x97C4, 0, 0x99C4, 0, 0x9BC4, 0, 0x9DC4, 0, 0x9FC4, 0, + 0xA1C4, 0, 0xA3C4, 0, 0xA5C4, 0, 0xA7C4, 0, 0xA9C4, 0, 0xABC4, 0, 0xADC4, 0, 0xAFC4, 0, + 0x69, 0, 0xB3C4, 0, 0xB5C4, 0, 0xB7C4, 0, 0, 0xBAC4, 0, 0xBCC4, 0, 0xBEC4, 0, 0x80C5, + 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, + }; + uint32 UTF8LC5[256] = { + 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, 0, + 0, 0x82C5, 0, 0x84C5, 0, 0x86C5, 0, 0x88C5, 0, 0, 0x8BC5, 0, 0x8DC5, 0, 0x8FC5, 0, + 0x91C5, 0, 0x93C5, 0, 0x95C5, 0, 0x97C5, 0, 0x99C5, 0, 0x9BC5, 0, 0x9DC5, 0, 0x9FC5, 0, + 0xA1C5, 0, 0xA3C5, 0, 0xA5C5, 0, 0xA7C5, 0, 0xA9C5, 0, 0xABC5, 0, 0xADC5, 0, 0xAFC5, 0, + 0xB1C5, 0, 0xB3C5, 0, 0xB5C5, 0, 0xB7C5, 0, 0xBFC3, 0xBAC5, 0, 0xBCC5, 0, 0xBEC5, 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, + }; + uint32 UTF8LC6[256] = { + 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, 0, + 0, 0x93C9, 0x83C6, 0, 0x85C6, 0, 0x94C9, 0x88C6, 0, 0, 0x97C9, 0x8CC6, 0, 0, 0x98C9, 0x99C9, + 0x9BC9, 0x92C6, 0, 0xA0C9, 0xA3C9, 0, 0xA9C9, 0xA8C9, 0x99C6, 0, 0, 0, 0xAFC9, 0xB2C9, 0, 0xB5C9, + 0xA1C6, 0, 0xA3C6, 0, 0xA5C6, 0, 0, 0xA8C6, 0, 0x83CA, 0, 0, 0xADC6, 0, 0x88CA, 0xB0C6, + 0, 0x8ACA, 0x8BCA, 0xB4C6, 0, 0xB6C6, 0, 0x92CA, 0xB9C6, 0, 0, 0, 0xBDC6, 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, + }; + uint32 UTF8LC7[256] = { + 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, 0, + 0, 0, 0, 0, 0x86C7, 0x86C7, 0, 0x89C7, 0x89C7, 0, 0x8CC7, 0x8CC7, 0, 0x8EC7, 0, 0x90C7, + 0, 0x92C7, 0, 0x94C7, 0, 0x96C7, 0, 0x98C7, 0, 0x9AC7, 0, 0x9CC7, 0, 0, 0x9FC7, 0, + 0xA1C7, 0, 0xA3C7, 0, 0xA5C7, 0, 0xA7C7, 0, 0xA9C7, 0, 0xABC7, 0, 0xADC7, 0, 0xAFC7, 0, + 0, 0xB3C7, 0, 0, 0xB5C7, 0, 0, 0, 0, 0, 0xBBC7, 0, 0xBDC7, 0, 0xBFC7, 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, + }; + uint32 UTF8LC8[256] = { + 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, 0, + 0x81C8, 0, 0x83C8, 0, 0x85C8, 0, 0x87C8, 0, 0x89C8, 0, 0x8BC8, 0, 0x8DC8, 0, 0x8FC8, 0, + 0x91C8, 0, 0x93C8, 0, 0x95C8, 0, 0x97C8, 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, + }; + uint32 UTF8LCE[256] = { + 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, 0, + 0, 0, 0, 0, 0, 0, 0xACCE, 0, 0xADCE, 0xAECE, 0xAFCE, 0, 0x8CCF, 0, 0x8DCF, 0x8ECF, + 0, 0xB1CE, 0xB2CE, 0xB3CE, 0xB4CE, 0xB5CE, 0xB6CE, 0xB7CE, 0xB8CE, 0xB9CE, 0xBACE, 0xBBCE, 0xBCCE, 0xBDCE, 0xBECE, 0xBFCE, + 0x80CF, 0x81CF, 0, 0x83CF, 0x84CF, 0x85CF, 0x86CF, 0x87CF, 0x88CF, 0x89CF, 0x8ACF, 0x8BCF, 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, + }; + uint32 UTF8LCF[256] = { + 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, 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, 0xA3CF, 0, 0xA5CF, 0, 0xA7CF, 0, 0xA9CF, 0, 0xABCF, 0, 0xADCF, 0, 0xAFCF, 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, + }; + uint32 UTF8LD0[256] = { + 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, 0, + 0, 0x91D1, 0x92D1, 0x93D1, 0x94D1, 0x95D1, 0x96D1, 0x97D1, 0x98D1, 0x99D1, 0x9AD1, 0x9BD1, 0x9CD1, 0, 0x9ED1, 0x9FD1, + 0xB0D0, 0xB1D0, 0xB2D0, 0xB3D0, 0xB4D0, 0xB5D0, 0xB6D0, 0xB7D0, 0xB8D0, 0xB9D0, 0xBAD0, 0xBBD0, 0xBCD0, 0xBDD0, 0xBED0, 0xBFD0, + 0x80D1, 0x81D1, 0x82D1, 0x83D1, 0x84D1, 0x85D1, 0x86D1, 0x87D1, 0x88D1, 0x89D1, 0x8AD1, 0x8BD1, 0x8CD1, 0x8DD1, 0x8ED1, 0x8FD1, + 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, + }; + uint32 UTF8LD1[256] = { + 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, 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, + 0xA1D1, 0, 0xA3D1, 0, 0xA5D1, 0, 0xA7D1, 0, 0xA9D1, 0, 0xABD1, 0, 0xADD1, 0, 0xAFD1, 0, + 0xB1D1, 0, 0xB3D1, 0, 0xB5D1, 0, 0xB7D1, 0, 0xB9D1, 0, 0xBBD1, 0, 0xBDD1, 0, 0xBFD1, 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, + }; + uint32 UTF8LD2[256] = { + 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, 0, + 0x81D2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0x91D2, 0, 0x93D2, 0, 0x95D2, 0, 0x97D2, 0, 0x99D2, 0, 0x9BD2, 0, 0x9DD2, 0, 0x9FD2, 0, + 0xA1D2, 0, 0xA3D2, 0, 0xA5D2, 0, 0xA7D2, 0, 0xA9D2, 0, 0xABD2, 0, 0xADD2, 0, 0xAFD2, 0, + 0xB1D2, 0, 0xB3D2, 0, 0xB5D2, 0, 0xB7D2, 0, 0xB9D2, 0, 0xBBD2, 0, 0xBDD2, 0, 0xBFD2, 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, + }; + uint32 UTF8LD3[256] = { + 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, 0, + 0, 0x82D3, 0, 0x84D3, 0, 0, 0, 0x88D3, 0, 0, 0, 0x8CD3, 0, 0, 0, 0, + 0x91D3, 0, 0x93D3, 0, 0x95D3, 0, 0x97D3, 0, 0x99D3, 0, 0x9BD3, 0, 0x9DD3, 0, 0x9FD3, 0, + 0xA1D3, 0, 0xA3D3, 0, 0xA5D3, 0, 0xA7D3, 0, 0xA9D3, 0, 0xABD3, 0, 0, 0, 0xAFD3, 0, + 0xB1D3, 0, 0xB3D3, 0, 0xB5D3, 0, 0, 0, 0xB9D3, 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, + }; + uint32 UTF8LD4[256] = { + 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, 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, 0xA1D5, 0xA2D5, 0xA3D5, 0xA4D5, 0xA5D5, 0xA6D5, 0xA7D5, 0xA8D5, 0xA9D5, 0xAAD5, 0xABD5, 0xACD5, 0xADD5, 0xAED5, 0xAFD5, + 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, + }; + uint32 UTF8LD5[256] = { + 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, 0, + 0xB0D5, 0xB1D5, 0xB2D5, 0xB3D5, 0xB4D5, 0xB5D5, 0xB6D5, 0xB7D5, 0xB8D5, 0xB9D5, 0xBAD5, 0xBBD5, 0xBCD5, 0xBDD5, 0xBED5, 0xBFD5, + 0x80D6, 0x81D6, 0x82D6, 0x83D6, 0x84D6, 0x85D6, 0x86D6, 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, + }; + uint32 UTF8LE182[256] = { + 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, 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, + 0x9083E1, 0x9183E1, 0x9283E1, 0x9383E1, 0x9483E1, 0x9583E1, 0x9683E1, 0x9783E1, 0x9883E1, 0x9983E1, 0x9A83E1, 0x9B83E1, 0x9C83E1, 0x9D83E1, 0x9E83E1, 0x9F83E1, + 0xA083E1, 0xA183E1, 0xA283E1, 0xA383E1, 0xA483E1, 0xA583E1, 0xA683E1, 0xA783E1, 0xA883E1, 0xA983E1, 0xAA83E1, 0xAB83E1, 0xAC83E1, 0xAD83E1, 0xAE83E1, 0xAF83E1, + 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, + }; + uint32 UTF8LE183[256] = { + 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, 0, + 0xB083E1, 0xB183E1, 0xB283E1, 0xB383E1, 0xB483E1, 0xB583E1, 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, + }; + uint32 UTF8LE1B8[256] = { + 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, 0, + 0x81B8E1, 0, 0x83B8E1, 0, 0x85B8E1, 0, 0x87B8E1, 0, 0x89B8E1, 0, 0x8BB8E1, 0, 0x8DB8E1, 0, 0x8FB8E1, 0, + 0x91B8E1, 0, 0x93B8E1, 0, 0x95B8E1, 0, 0x97B8E1, 0, 0x99B8E1, 0, 0x9BB8E1, 0, 0x9DB8E1, 0, 0x9FB8E1, 0, + 0xA1B8E1, 0, 0xA3B8E1, 0, 0xA5B8E1, 0, 0xA7B8E1, 0, 0xA9B8E1, 0, 0xABB8E1, 0, 0xADB8E1, 0, 0xAFB8E1, 0, + 0xB1B8E1, 0, 0xB3B8E1, 0, 0xB5B8E1, 0, 0xB7B8E1, 0, 0xB9B8E1, 0, 0xBBB8E1, 0, 0xBDB8E1, 0, 0xBFB8E1, 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, + }; + uint32 UTF8LE1B9[256] = { + 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, 0, + 0x81B9E1, 0, 0x83B9E1, 0, 0x85B9E1, 0, 0x87B9E1, 0, 0x89B9E1, 0, 0x8BB9E1, 0, 0x8DB9E1, 0, 0x8FB9E1, 0, + 0x91B9E1, 0, 0x93B9E1, 0, 0x95B9E1, 0, 0x97B9E1, 0, 0x99B9E1, 0, 0x9BB9E1, 0, 0x9DB9E1, 0, 0x9FB9E1, 0, + 0xA1B9E1, 0, 0xA3B9E1, 0, 0xA5B9E1, 0, 0xA7B9E1, 0, 0xA9B9E1, 0, 0xABB9E1, 0, 0xADB9E1, 0, 0xAFB9E1, 0, + 0xB1B9E1, 0, 0xB3B9E1, 0, 0xB5B9E1, 0, 0xB7B9E1, 0, 0xB9B9E1, 0, 0xBBB9E1, 0, 0xBDB9E1, 0, 0xBFB9E1, 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, + }; + uint32 UTF8LE1BA[256] = { + 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, 0, + 0x81BAE1, 0, 0x83BAE1, 0, 0x85BAE1, 0, 0x87BAE1, 0, 0x89BAE1, 0, 0x8BBAE1, 0, 0x8DBAE1, 0, 0x8FBAE1, 0, + 0x91BAE1, 0, 0x93BAE1, 0, 0x95BAE1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0xA1BAE1, 0, 0xA3BAE1, 0, 0xA5BAE1, 0, 0xA7BAE1, 0, 0xA9BAE1, 0, 0xABBAE1, 0, 0xADBAE1, 0, 0xAFBAE1, 0, + 0xB1BAE1, 0, 0xB3BAE1, 0, 0xB5BAE1, 0, 0xB7BAE1, 0, 0xB9BAE1, 0, 0xBBBAE1, 0, 0xBDBAE1, 0, 0xBFBAE1, 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, + }; + uint32 UTF8LE1BB[256] = { + 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, 0, + 0x81BBE1, 0, 0x83BBE1, 0, 0x85BBE1, 0, 0x87BBE1, 0, 0x89BBE1, 0, 0x8BBBE1, 0, 0x8DBBE1, 0, 0x8FBBE1, 0, + 0x91BBE1, 0, 0x93BBE1, 0, 0x95BBE1, 0, 0x97BBE1, 0, 0x99BBE1, 0, 0x9BBBE1, 0, 0x9DBBE1, 0, 0x9FBBE1, 0, + 0xA1BBE1, 0, 0xA3BBE1, 0, 0xA5BBE1, 0, 0xA7BBE1, 0, 0xA9BBE1, 0, 0xABBBE1, 0, 0xADBBE1, 0, 0xAFBBE1, 0, + 0xB1BBE1, 0, 0xB3BBE1, 0, 0xB5BBE1, 0, 0xB7BBE1, 0, 0xB9BBE1, 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, + }; + uint32 UTF8LE1BC[256] = { + 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, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0x80BCE1, 0x81BCE1, 0x82BCE1, 0x83BCE1, 0x84BCE1, 0x85BCE1, 0x86BCE1, 0x87BCE1, + 0, 0, 0, 0, 0, 0, 0, 0, 0x90BCE1, 0x91BCE1, 0x92BCE1, 0x93BCE1, 0x94BCE1, 0x95BCE1, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0xA0BCE1, 0xA1BCE1, 0xA2BCE1, 0xA3BCE1, 0xA4BCE1, 0xA5BCE1, 0xA6BCE1, 0xA7BCE1, + 0, 0, 0, 0, 0, 0, 0, 0, 0xB0BCE1, 0xB1BCE1, 0xB2BCE1, 0xB3BCE1, 0xB4BCE1, 0xB5BCE1, 0xB6BCE1, 0xB7BCE1, + 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, + }; + uint32 UTF8LE1BD[256] = { + 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, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0x80BDE1, 0x81BDE1, 0x82BDE1, 0x83BDE1, 0x84BDE1, 0x85BDE1, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x91BDE1, 0, 0x93BDE1, 0, 0x95BDE1, 0, 0x97BDE1, + 0, 0, 0, 0, 0, 0, 0, 0, 0xA0BDE1, 0xA1BDE1, 0xA2BDE1, 0xA3BDE1, 0xA4BDE1, 0xA5BDE1, 0xA6BDE1, 0xA7BDE1, + 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, + }; + uint32 UTF8LE1BE[256] = { + 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, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0x80BEE1, 0x81BEE1, 0x82BEE1, 0x83BEE1, 0x84BEE1, 0x85BEE1, 0x86BEE1, 0x87BEE1, + 0, 0, 0, 0, 0, 0, 0, 0, 0x90BEE1, 0x91BEE1, 0x92BEE1, 0x93BEE1, 0x94BEE1, 0x95BEE1, 0x96BEE1, 0x97BEE1, + 0, 0, 0, 0, 0, 0, 0, 0, 0xA0BEE1, 0xA1BEE1, 0xA2BEE1, 0xA3BEE1, 0xA4BEE1, 0xA5BEE1, 0xA6BEE1, 0xA7BEE1, + 0, 0, 0, 0, 0, 0, 0, 0, 0xB0BEE1, 0xB1BEE1, 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, + }; + uint32 UTF8LE1BF[256] = { + 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, 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, 0x90BFE1, 0x91BFE1, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0xA0BFE1, 0xA1BFE1, 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, + }; + uint32 UTF8LE1[256] = { + 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, 0, + 0, 0, (uint32)&UTF8LE182, (uint32)&UTF8LE183, 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, (uint32)&UTF8LE1B8, (uint32)&UTF8LE1B9, (uint32)&UTF8LE1BA, (uint32)&UTF8LE1BB, (uint32)&UTF8LE1BC, (uint32)&UTF8LE1BD, (uint32)&UTF8LE1BE, (uint32)&UTF8LE1BF, + 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, + }; + uint32 UTF8LE292[256] = { + 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, 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, 0x9093E2, 0x9193E2, 0x9293E2, 0x9393E2, 0x9493E2, 0x9593E2, 0x9693E2, 0x9793E2, 0x9893E2, 0x9993E2, + 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, + }; + uint32 UTF8LE293[256] = { + 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, 0, + 0x9A93E2, 0x9B93E2, 0x9C93E2, 0x9D93E2, 0x9E93E2, 0x9F93E2, 0xA093E2, 0xA193E2, 0xA293E2, 0xA393E2, 0xA493E2, 0xA593E2, 0xA693E2, 0xA793E2, 0xA893E2, 0xA993E2, + 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, + }; + uint32 UTF8LE2[256] = { + 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, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, (uint32)&UTF8LE292, (uint32)&UTF8LE293, 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, + }; + uint32 UTF8LEFBC[256] = { + 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, 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, 0x81BDEF, 0x82BDEF, 0x83BDEF, 0x84BDEF, 0x85BDEF, 0x86BDEF, 0x87BDEF, 0x88BDEF, 0x89BDEF, 0x8ABDEF, 0x8BBDEF, 0x8CBDEF, 0x8DBDEF, 0x8EBDEF, 0x8FBDEF, + 0x90BDEF, 0x91BDEF, 0x92BDEF, 0x93BDEF, 0x94BDEF, 0x95BDEF, 0x96BDEF, 0x97BDEF, 0x98BDEF, 0x99BDEF, 0x9ABDEF, 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, + }; + uint32 UTF8LEF[256] = { + 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, 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, (uint32)&UTF8LEFBC, 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, + }; + uint32 tf_lower[256] = { + 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, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, + 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 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, (uint32)&UTF8LC3, (uint32)&UTF8LC4, (uint32)&UTF8LC5, (uint32)&UTF8LC6, (uint32)&UTF8LC7, (uint32)&UTF8LC8, 0, 0, 0, 0, 0, (uint32)&UTF8LCE, (uint32)&UTF8LCF, + (uint32)&UTF8LD0, (uint32)&UTF8LD1, (uint32)&UTF8LD2, (uint32)&UTF8LD3, (uint32)&UTF8LD4, (uint32)&UTF8LD5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, (uint32)&UTF8LE1, (uint32)&UTF8LE2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, (uint32)&UTF8LEF, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + }; + + uint32 UTF8UC3[256] = { + 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, 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, + 0x80C3, 0x81C3, 0x82C3, 0x83C3, 0x84C3, 0x85C3, 0x86C3, 0x87C3, 0x88C3, 0x89C3, 0x8AC3, 0x8BC3, 0x8CC3, 0x8DC3, 0x8EC3, 0x8FC3, + 0x90C3, 0x91C3, 0x92C3, 0x93C3, 0x94C3, 0x95C3, 0x96C3, 0, 0x98C3, 0x99C3, 0x9AC3, 0x9BC3, 0x9CC3, 0x9DC3, 0x9EC3, 0xB8C5, + 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, + }; + uint32 UTF8UC4[256] = { + 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, 0, + 0, 0x80C4, 0, 0x82C4, 0, 0x84C4, 0, 0x86C4, 0, 0x88C4, 0, 0x8AC4, 0, 0x8CC4, 0, 0x8EC4, + 0, 0x90C4, 0, 0x92C4, 0, 0x94C4, 0, 0x96C4, 0, 0x98C4, 0, 0x9AC4, 0, 0x9CC4, 0, 0x9EC4, + 0, 0xA0C4, 0, 0xA2C4, 0, 0xA4C4, 0, 0xA6C4, 0, 0xA8C4, 0, 0xAAC4, 0, 0xACC4, 0, 0xAEC4, + 0, 0x49, 0, 0xB2C4, 0, 0xB4C4, 0, 0xB6C4, 0, 0, 0xB9C4, 0, 0xBBC4, 0, 0xBDC4, 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, + }; + uint32 UTF8UC5[256] = { + 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, 0, + 0xBFC4, 0, 0x81C5, 0, 0x83C5, 0, 0x85C5, 0, 0x87C5, 0, 0, 0x8AC5, 0, 0x8CC5, 0, 0x8EC5, + 0, 0x90C5, 0, 0x92C5, 0, 0x94C5, 0, 0x96C5, 0, 0x98C5, 0, 0x9AC5, 0, 0x9CC5, 0, 0x9EC5, + 0, 0xA0C5, 0, 0xA2C5, 0, 0xA4C5, 0, 0xA6C5, 0, 0xA8C5, 0, 0xAAC5, 0, 0xACC5, 0, 0xAEC5, + 0, 0xB0C5, 0, 0xB2C5, 0, 0xB4C5, 0, 0xB6C5, 0, 0, 0xB9C5, 0, 0xBBC5, 0, 0xBDC5, 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, + }; + uint32 UTF8UC6[256] = { + 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, 0, + 0, 0, 0, 0x82C6, 0, 0x84C6, 0, 0, 0x87C6, 0, 0, 0, 0x8BC6, 0, 0, 0, + 0, 0, 0x91C6, 0, 0, 0, 0, 0, 0, 0x98C6, 0, 0, 0, 0, 0, 0, + 0, 0xA0C6, 0, 0xA2C6, 0, 0xA4C6, 0, 0, 0xA7C6, 0, 0, 0, 0, 0xACC6, 0, 0, + 0xAFC6, 0, 0, 0, 0xB3C6, 0, 0xB5C6, 0, 0, 0xB8C6, 0, 0, 0, 0xBCC6, 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, + }; + uint32 UTF8UC7[256] = { + 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, 0, + 0, 0, 0, 0, 0, 0, 0x84C7, 0, 0, 0x87C7, 0, 0, 0x8AC7, 0, 0x8DC7, 0, + 0x8FC7, 0, 0x91C7, 0, 0x93C7, 0, 0x95C7, 0, 0x97C7, 0, 0x99C7, 0, 0x9BC7, 0, 0, 0x9EC7, + 0, 0xA0C7, 0, 0xA2C7, 0, 0xA4C7, 0, 0xA6C7, 0, 0xA8C7, 0, 0xAAC7, 0, 0xACC7, 0, 0xAEC7, + 0, 0, 0, 0xB1C7, 0, 0xB4C7, 0, 0, 0, 0, 0, 0xBAC7, 0, 0xBCC7, 0, 0xBEC7, + 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, + }; + uint32 UTF8UC8[256] = { + 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, 0, + 0, 0x80C8, 0, 0x82C8, 0, 0x84C8, 0, 0x86C8, 0, 0x88C8, 0, 0x8AC8, 0, 0x8CC8, 0, 0x8EC8, + 0, 0x90C8, 0, 0x92C8, 0, 0x94C8, 0, 0x96C8, 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, + }; + uint32 UTF8UC9[256] = { + 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, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0x81C6, 0x86C6, 0, 0, 0x8AC6, 0x8EC6, 0x8FC6, 0, 0x90C6, 0, 0, 0, 0, + 0x93C6, 0, 0, 0x94C6, 0, 0, 0, 0, 0x97C6, 0x96C6, 0, 0, 0, 0, 0, 0x9CC6, + 0, 0, 0x9DC6, 0, 0, 0x9FC6, 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, + }; + uint32 UTF8UCA[256] = { + 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, 0, + 0, 0, 0, 0xA9C6, 0, 0, 0, 0, 0xAEC6, 0, 0xB1C6, 0xB2C6, 0, 0, 0, 0, + 0, 0, 0xB7C6, 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, + }; + uint32 UTF8UCE[256] = { + 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, 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, 0x86CE, 0x88CE, 0x89CE, 0x8ACE, + 0, 0x91CE, 0x92CE, 0x93CE, 0x94CE, 0x95CE, 0x96CE, 0x97CE, 0x98CE, 0x99CE, 0x9ACE, 0x9BCE, 0x9CCE, 0x9DCE, 0x9ECE, 0x9FCE, + 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, + }; + uint32 UTF8UCF[256] = { + 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, 0, + 0xA0CE, 0xA1CE, 0, 0xA3CE, 0xA4CE, 0xA5CE, 0xA6CE, 0xA7CE, 0xA8CE, 0xA9CE, 0xAACE, 0xABCE, 0x8CCE, 0x8ECE, 0x8FCE, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0xA2CF, 0, 0xA4CF, 0, 0xA6CF, 0, 0xA8CF, 0, 0xAACF, 0, 0xACCF, 0, 0xAECF, + 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, + }; + uint32 UTF8UD0[256] = { + 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, 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, + 0x90D0, 0x91D0, 0x92D0, 0x93D0, 0x94D0, 0x95D0, 0x96D0, 0x97D0, 0x98D0, 0x99D0, 0x9AD0, 0x9BD0, 0x9CD0, 0x9DD0, 0x9ED0, 0x9FD0, + 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, + }; + uint32 UTF8UD1[256] = { + 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, 0, + 0xA0D0, 0xA1D0, 0xA2D0, 0xA3D0, 0xA4D0, 0xA5D0, 0xA6D0, 0xA7D0, 0xA8D0, 0xA9D0, 0xAAD0, 0xABD0, 0xACD0, 0xADD0, 0xAED0, 0xAFD0, + 0, 0x81D0, 0x82D0, 0x83D0, 0x84D0, 0x85D0, 0x86D0, 0x87D0, 0x88D0, 0x89D0, 0x8AD0, 0x8BD0, 0x8CD0, 0, 0x8ED0, 0x8FD0, + 0, 0xA0D1, 0, 0xA2D1, 0, 0xA4D1, 0, 0xA6D1, 0, 0xA8D1, 0, 0xAAD1, 0, 0xACD1, 0, 0xAED1, + 0, 0xB0D1, 0, 0xB2D1, 0, 0xB4D1, 0, 0xB6D1, 0, 0xB8D1, 0, 0xBAD1, 0, 0xBCD1, 0, 0xBED1, + 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, + }; + uint32 UTF8UD2[256] = { + 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, 0, + 0, 0x80D2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0x90D2, 0, 0x92D2, 0, 0x94D2, 0, 0x96D2, 0, 0x98D2, 0, 0x9AD2, 0, 0x9CD2, 0, 0x9ED2, + 0, 0xA0D2, 0, 0xA2D2, 0, 0xA4D2, 0, 0xA6D2, 0, 0xA8D2, 0, 0xAAD2, 0, 0xACD2, 0, 0xAED2, + 0, 0xB0D2, 0, 0xB2D2, 0, 0xB4D2, 0, 0xB6D2, 0, 0xB8D2, 0, 0xBAD2, 0, 0xBCD2, 0, 0xBED2, + 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, + }; + uint32 UTF8UD3[256] = { + 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, 0, + 0, 0, 0x81D3, 0, 0x83D3, 0, 0, 0, 0x87D3, 0, 0, 0, 0x8BD3, 0, 0, 0, + 0, 0x90D3, 0, 0x92D3, 0, 0x94D3, 0, 0x96D3, 0, 0x98D3, 0, 0x9AD3, 0, 0x9CD3, 0, 0x9ED3, + 0, 0xA0D3, 0, 0xA2D3, 0, 0xA4D3, 0, 0xA6D3, 0, 0xA8D3, 0, 0xAAD3, 0, 0, 0, 0xAED3, + 0, 0xB0D3, 0, 0xB2D3, 0, 0xB4D3, 0, 0, 0, 0xB8D3, 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, + }; + uint32 UTF8UD5[256] = { + 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, 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, 0xB1D4, 0xB2D4, 0xB3D4, 0xB4D4, 0xB5D4, 0xB6D4, 0xB7D4, 0xB8D4, 0xB9D4, 0xBAD4, 0xBBD4, 0xBCD4, 0xBDD4, 0xBED4, 0xBFD4, + 0x80D5, 0x81D5, 0x82D5, 0x83D5, 0x84D5, 0x85D5, 0x86D5, 0x87D5, 0x88D5, 0x89D5, 0x8AD5, 0x8BD5, 0x8CD5, 0x8DD5, 0x8ED5, 0x8FD5, + 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, + }; + uint32 UTF8UD6[256] = { + 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, 0, + 0x90D5, 0x91D5, 0x92D5, 0x93D5, 0x94D5, 0x95D5, 0x96D5, 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, + }; + uint32 UTF8UE183[256] = { + 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, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0xA082E1, 0xA182E1, 0xA282E1, 0xA382E1, 0xA482E1, 0xA582E1, 0xA682E1, 0xA782E1, 0xA882E1, 0xA982E1, 0xAA82E1, 0xAB82E1, 0xAC82E1, 0xAD82E1, 0xAE82E1, 0xAF82E1, + 0xB082E1, 0xB182E1, 0xB282E1, 0xB382E1, 0xB482E1, 0xB582E1, 0xB682E1, 0xB782E1, 0xB882E1, 0xB982E1, 0xBA82E1, 0xBB82E1, 0xBC82E1, 0xBD82E1, 0xBE82E1, 0xBF82E1, + 0x8083E1, 0x8183E1, 0x8283E1, 0x8383E1, 0x8483E1, 0x8583E1, 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, + }; + uint32 UTF8UE1B8[256] = { + 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, 0, + 0, 0x80B8E1, 0, 0x82B8E1, 0, 0x84B8E1, 0, 0x86B8E1, 0, 0x88B8E1, 0, 0x8AB8E1, 0, 0x8CB8E1, 0, 0x8EB8E1, + 0, 0x90B8E1, 0, 0x92B8E1, 0, 0x94B8E1, 0, 0x96B8E1, 0, 0x98B8E1, 0, 0x9AB8E1, 0, 0x9CB8E1, 0, 0x9EB8E1, + 0, 0xA0B8E1, 0, 0xA2B8E1, 0, 0xA4B8E1, 0, 0xA6B8E1, 0, 0xA8B8E1, 0, 0xAAB8E1, 0, 0xACB8E1, 0, 0xAEB8E1, + 0, 0xB0B8E1, 0, 0xB2B8E1, 0, 0xB4B8E1, 0, 0xB6B8E1, 0, 0xB8B8E1, 0, 0xBAB8E1, 0, 0xBCB8E1, 0, 0xBEB8E1, + 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, + }; + uint32 UTF8UE1B9[256] = { + 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, 0, + 0, 0x80B9E1, 0, 0x82B9E1, 0, 0x84B9E1, 0, 0x86B9E1, 0, 0x88B9E1, 0, 0x8AB9E1, 0, 0x8CB9E1, 0, 0x8EB9E1, + 0, 0x90B9E1, 0, 0x92B9E1, 0, 0x94B9E1, 0, 0x96B9E1, 0, 0x98B9E1, 0, 0x9AB9E1, 0, 0x9CB9E1, 0, 0x9EB9E1, + 0, 0xA0B9E1, 0, 0xA2B9E1, 0, 0xA4B9E1, 0, 0xA6B9E1, 0, 0xA8B9E1, 0, 0xAAB9E1, 0, 0xACB9E1, 0, 0xAEB9E1, + 0, 0xB0B9E1, 0, 0xB2B9E1, 0, 0xB4B9E1, 0, 0xB6B9E1, 0, 0xB8B9E1, 0, 0xBAB9E1, 0, 0xBCB9E1, 0, 0xBEB9E1, + 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, + }; + uint32 UTF8UE1BA[256] = { + 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, 0, + 0, 0x80BAE1, 0, 0x82BAE1, 0, 0x84BAE1, 0, 0x86BAE1, 0, 0x88BAE1, 0, 0x8ABAE1, 0, 0x8CBAE1, 0, 0x8EBAE1, + 0, 0x90BAE1, 0, 0x92BAE1, 0, 0x94BAE1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0xA0BAE1, 0, 0xA2BAE1, 0, 0xA4BAE1, 0, 0xA6BAE1, 0, 0xA8BAE1, 0, 0xAABAE1, 0, 0xACBAE1, 0, 0xAEBAE1, + 0, 0xB0BAE1, 0, 0xB2BAE1, 0, 0xB4BAE1, 0, 0xB6BAE1, 0, 0xB8BAE1, 0, 0xBABAE1, 0, 0xBCBAE1, 0, 0xBEBAE1, + 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, + }; + uint32 UTF8UE1BB[256] = { + 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, 0, + 0, 0x80BBE1, 0, 0x82BBE1, 0, 0x84BBE1, 0, 0x86BBE1, 0, 0x88BBE1, 0, 0x8ABBE1, 0, 0x8CBBE1, 0, 0x8EBBE1, + 0, 0x90BBE1, 0, 0x92BBE1, 0, 0x94BBE1, 0, 0x96BBE1, 0, 0x98BBE1, 0, 0x9ABBE1, 0, 0x9CBBE1, 0, 0x9EBBE1, + 0, 0xA0BBE1, 0, 0xA2BBE1, 0, 0xA4BBE1, 0, 0xA6BBE1, 0, 0xA8BBE1, 0, 0xAABBE1, 0, 0xACBBE1, 0, 0xAEBBE1, + 0, 0xB0BBE1, 0, 0xB2BBE1, 0, 0xB4BBE1, 0, 0xB6BBE1, 0, 0xB8BBE1, 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, + }; + uint32 UTF8UE1BC[256] = { + 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, 0, + 0x88BCE1, 0x89BCE1, 0x8ABCE1, 0x8BBCE1, 0x8CBCE1, 0x8DBCE1, 0x8EBCE1, 0x8FBCE1, 0, 0, 0, 0, 0, 0, 0, 0, + 0x98BCE1, 0x99BCE1, 0x9ABCE1, 0x9BBCE1, 0x9CBCE1, 0x9DBCE1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0xA8BCE1, 0xA9BCE1, 0xAABCE1, 0xABBCE1, 0xACBCE1, 0xADBCE1, 0xAEBCE1, 0xAFBCE1, 0, 0, 0, 0, 0, 0, 0, 0, + 0xB8BCE1, 0xB9BCE1, 0xBABCE1, 0xBBBCE1, 0xBCBCE1, 0xBDBCE1, 0xBEBCE1, 0xBFBCE1, 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, + }; + uint32 UTF8UE1BD[256] = { + 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, 0, + 0x88BDE1, 0x89BDE1, 0x8ABDE1, 0x8BBDE1, 0x8CBDE1, 0x8DBDE1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0x99BDE1, 0, 0x9BBDE1, 0, 0x9DBDE1, 0, 0x9FBDE1, 0, 0, 0, 0, 0, 0, 0, 0, + 0xA8BDE1, 0xA9BDE1, 0xAABDE1, 0xABBDE1, 0xACBDE1, 0xADBDE1, 0xAEBDE1, 0xAFBDE1, 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, + }; + uint32 UTF8UE1BE[256] = { + 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, 0, + 0x88BEE1, 0x89BEE1, 0x8ABEE1, 0x8BBEE1, 0x8CBEE1, 0x8DBEE1, 0x8EBEE1, 0x8FBEE1, 0, 0, 0, 0, 0, 0, 0, 0, + 0x98BEE1, 0x99BEE1, 0x9ABEE1, 0x9BBEE1, 0x9CBEE1, 0x9DBEE1, 0x9EBEE1, 0x9FBEE1, 0, 0, 0, 0, 0, 0, 0, 0, + 0xA8BEE1, 0xA9BEE1, 0xAABEE1, 0xABBEE1, 0xACBEE1, 0xADBEE1, 0xAEBEE1, 0xAFBEE1, 0, 0, 0, 0, 0, 0, 0, 0, + 0xB8BEE1, 0xB9BEE1, 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, + }; + uint32 UTF8UE1BF[256] = { + 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, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0x98BFE1, 0x99BFE1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0xA8BFE1, 0xA9BFE1, 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, + }; + uint32 UTF8UE1[256] = { + 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, 0, + 0, 0, 0, (uint32)&UTF8UE183, 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, (uint32)&UTF8UE1B8, (uint32)&UTF8UE1B9, (uint32)&UTF8UE1BA, (uint32)&UTF8UE1BB, (uint32)&UTF8UE1BC, (uint32)&UTF8UE1BD, (uint32)&UTF8UE1BE, (uint32)&UTF8UE1BF, + 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, + }; + uint32 UTF8UE293[256] = { + 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, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0xB692E2, 0xB792E2, 0xB892E2, 0xB992E2, 0xBA92E2, 0xBB92E2, 0xBC92E2, 0xBD92E2, 0xBE92E2, 0xBF92E2, 0x8093E2, 0x8193E2, 0x8293E2, 0x8393E2, 0x8493E2, 0x8593E2, + 0x8693E2, 0x8793E2, 0x8893E2, 0x8993E2, 0x8A93E2, 0x8B93E2, 0x8C93E2, 0x8D93E2, 0x8E93E2, 0x8F93E2, 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, + }; + uint32 UTF8UE2[256] = { + 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, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, (uint32)&UTF8UE293, 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, + }; + uint32 UTF8UEFBD[256] = { + 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, 0, + 0, 0xA1BCEF, 0xA2BCEF, 0xA3BCEF, 0xA4BCEF, 0xA5BCEF, 0xA6BCEF, 0xA7BCEF, 0xA8BCEF, 0xA9BCEF, 0xAABCEF, 0xABBCEF, 0xACBCEF, 0xADBCEF, 0xAEBCEF, 0xAFBCEF, + 0xB0BCEF, 0xB1BCEF, 0xB2BCEF, 0xB3BCEF, 0xB4BCEF, 0xB5BCEF, 0xB6BCEF, 0xB7BCEF, 0xB8BCEF, 0xB9BCEF, 0xBABCEF, 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, + }; + uint32 UTF8UEF[256] = { + 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, 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, (uint32)&UTF8UEFBD, 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, + }; + uint32 tf_upper[256] = { + 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, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, + 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 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, (uint32)&UTF8UC3, (uint32)&UTF8UC4, (uint32)&UTF8UC5, (uint32)&UTF8UC6, (uint32)&UTF8UC7, (uint32)&UTF8UC8, (uint32)&UTF8UC9, (uint32)&UTF8UCA, 0, 0, 0, (uint32)&UTF8UCE, (uint32)&UTF8UCF, + (uint32)&UTF8UD0, (uint32)&UTF8UD1, (uint32)&UTF8UD2, (uint32)&UTF8UD3, 0, (uint32)&UTF8UD5, (uint32)&UTF8UD6, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, (uint32)&UTF8UE1, (uint32)&UTF8UE2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, (uint32)&UTF8UEF, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + }; + + uint32 transform(uint8_const_ptr* ptr, uint32* table) + { + if ((**ptr & 0x80) && (**ptr & 0xF8) != 0xF8) { + uint32 result = 0; + uint8 head = **ptr; + uint8* dst = (uint8*)&result; + while ((head & 0x80) && **ptr) { + if (table) + table = (uint32*)table[**ptr]; + *dst++ = *(*ptr)++; + head <<= 1; + } + if (table && (head & 0x80) == 0) { + result = (uint32)table; + } + return result; + } else { + if (table && table[**ptr]) { + return table[*(*ptr)++]; + } + return *(*ptr)++; + } + } + + uint8_const_ptr next(uint8_const_ptr ptr) + { + if ((*ptr & 0x80) && (*ptr & 0xF8) != 0xF8) { + uint8 head = *ptr; + while ((head & 0x80) & *ptr) { + head <<= 1; + ptr++; + } + return ptr; + } else { + return ptr + 1; + } + } + + uint32 parse(uint32 cp) { + if ((cp & ~0x7F) == 0) { + return cp; + } + uint32 result = (cp & 0xFF); + uint32 mask = 0x40; + cp >>= 8; + while (cp) { + result = (result << 6) | (cp & 0x3F); + cp >>= 8; + mask <<= 5; + } + return result & (mask - 1); + } + +} diff --git a/src/base/utf8.h b/src/base/utf8.h new file mode 100644 index 0000000..6624366 --- /dev/null +++ b/src/base/utf8.h @@ -0,0 +1,18 @@ +#pragma once + +#include "types.h" + +namespace utf8 { + + extern uint32 tf_lower[256]; + extern uint32 tf_upper[256]; + + uint32 transform(uint8_const_ptr* ptr, uint32* table); + inline uint32 transform(uint8_const_ptr ptr, uint32* table) { + return transform(&ptr, table); + } + uint8_const_ptr next(uint8_const_ptr ptr); + + uint32 parse(uint32 cp); + +} diff --git a/src/data.cpp b/src/data.cpp new file mode 100644 index 0000000..58ff3aa --- /dev/null +++ b/src/data.cpp @@ -0,0 +1,597 @@ +#define NOMINMAX +#include "base/http.h" +#include "data.h" +#include "base/thread.h" +#include "app.h" +#include "wizard.h" +#include +#include + +void ProgramData::Task::stop() { + terminate_ = true; + if (thread_) { + if (WaitForSingleObject(thread_, INFINITE) != WAIT_OBJECT_0) { + // don't do this, it will likely crash everything + TerminateThread(thread_, 0); + } + CloseHandle(thread_); + thread_ = nullptr; + } +} +ProgramData::Task::~Task() { + stop(); +} + +void ProgramData::Task::start(ProgramData* data) { + data_ = data; + callback_ = data->app_->wizard()->page(); + thread_ = thread::create(this, &Task::proc); +} + +void ProgramData::Task::notify(uint32 value) { + if (!terminate_ && callback_) { + uint32 time = GetTickCount(); + if (time > lastNotify_ + 500 || value != lastNotifyCode_) { + PostMessage(data_->app_->wizard()->getHandle(), WM_TASKDONE, (WPARAM)callback_, value); + lastNotify_ = time; + lastNotifyCode_ = value; + if (lastNotifyCode_ >= LOADING) lastNotifyCode_ = 0; + } + } +} +int ProgramData::Task::proc() { + try { + uint32 res = run(); + notify(res); + } catch (Exception& ex) { + notify(-1); + } + return 0; +} + +void ProgramData::setTask(Task* task) { + // duplicating the unique_ptr just in case + { + auto tsk = task_; + if (tsk) tsk->stop(); + } + task_.reset(task); + if (task) task->start(this); +} + +//////////////////////////// + +void ProgramData::setProgram(std::string const& program) { + class ProgramTask : public Task { + public: + ProgramTask(std::string const& program) + : program_(program) + {} + private: + std::string program_; + uint32 run() { + NGDP::NGDP* ngdp = new NGDP::NGDP(program_); + if (ngdp && ngdp->regions().empty()) { + delete ngdp; + ngdp = ngdp; + } + if (!terminate_) { + data_->ngdp_.reset(ngdp); + } + return 0; + } + }; + + if (!program.empty()) { + setTask(new ProgramTask(program)); + } else { + setTask(nullptr); + ngdp_.reset(); + } +} + +////////////////////////////////// + +void ProgramData::loadBuilds() { + class BuildTask : public Task { + private: + uint32 run() { + if (!data_->ngdp_->version()) return -1; + std::string cdn_hash = data_->ngdp_->version()->cdn; + File file = data_->ngdp_->load(cdn_hash); + if (!file) return -1; + data_->cdn_config = NGDP::ParseConfig(file); + data_->builds = split(data_->cdn_config["builds"]); + notify(0); + for (size_t i = 0; i < data_->builds.size() && !terminate_; ++i) { + std::string build = data_->builds[i]; + File file = data_->ngdp_->load(build); + if (!file) continue; + data_->build_configs[build] = NGDP::ParseConfig(file); + notify(i + 1); + } + if (!terminate_) { + data_->builds_loaded = cdn_hash; + } + return data_->builds.size(); + } + }; + + builds_loaded.clear(); + setTask(new BuildTask); +} + +///////////////////////////////////// + +File ProgramData::Task::loadData(std::string const& hash, int idx) { + File file = NGDP::CascStorage::getCache(hash); + if (file) return file; + file = data_->ngdp_->load(hash, "data"); + if (!file) return file; + + data_->loading_size = file.size(); + if (data_->loading_size >= (1 << 20)) { + static uint8 buffer[1 << 18]; + while (file.tell() < data_->loading_size && !terminate_) { + file.read(buffer, sizeof buffer); + data_->loading_progress = file.tell(); + notify(LOADING + idx); + } + } + + if (file) NGDP::CascStorage::addCache(hash, file); + return file; +} + +std::vector ProgramData::loadTags(File& file, uint32 numTags, uint32 numEntries) { + std::vector result; + uint32 maskSize = (numEntries + 7) / 8; + while (numTags--) { + result.emplace_back(); + auto& tag = result.back(); + while (char chr = file.getc()) { + tag.name.push_back(chr); + } + tag.type = file.read16(true); + tag.mask.resize(maskSize); + file.read(&tag.mask[0], maskSize); + } + return result; +} + +void ProgramData::loadTags() { + class TagTask : public Task { + private: + uint32 run() { + // get build config + if (!data_->build_configs.count(data_->selected_build)) return -1; + auto buildConfig = data_->build_configs[data_->selected_build]; + + // load encoding file + File file = loadData(split(buildConfig["encoding"])[1], 0); + if (!file || terminate_) return -1; + data_->encoding_.reset(new NGDP::Encoding(NGDP::DecodeBLTE(file, std::stoi(split(buildConfig["encoding-size"])[0])))); + + // get download CDN hash + NGDP::Hash hash; + NGDP::from_string(hash, buildConfig["download"]); + auto const* enc = data_->encoding_->getEncoding(hash); + if (!enc) return -1; + + // load download file + file = loadData(NGDP::to_string(enc->keys[0]), 1); + if (!file || terminate_) return -1; + file = NGDP::DecodeBLTE(file, enc->usize); + + // parse download file + if (file.read16(true) != 'DL') return -1; + file.seek(3, SEEK_CUR); + uint32 numEntries = file.read32(true); + uint16 numTags = file.read16(true); + data_->file_sizes_.clear(); + for (uint32 i = 0; i < numEntries; ++i) { + file.read(hash, sizeof hash); + file.seek(10, SEEK_CUR); + auto const* layout = data_->encoding_->getLayout(hash); + data_->file_sizes_.push_back(layout ? layout->csize : 0); + } + data_->tags_ = ProgramData::loadTags(file, numTags, numEntries); + std::sort(data_->tags_.begin(), data_->tags_.end(), [](Tag const& lhs, Tag const& rhs) { + return lhs.type < rhs.type; + }); + std::vector> tags; + for (size_t i = 0; i < data_->tags_.size(); ++i) { + if (i == 0 || data_->tags_[i].type != data_->tags_[i - 1].type) { + tags.emplace_back(); + } + tags.back().push_back(data_->tags_[i].name); + } + data_->tags = tags; + return 0; + } + }; + + tags.clear(); + setTask(new TagTask); +} + +std::vector ProgramData::downloadMask() { + size_t maskSize = (file_sizes_.size() + 7) / 8; + std::vector mask(maskSize, 0); + for (std::string const& tags : used_tags) { + std::vector cur_mask(maskSize, 0xFF); + for (std::string const& tag : split(tags)) { + for (size_t i = 0; i < tags_.size(); ++i) { + if (tags_[i].name == tag) { + for (size_t j = 0; j < maskSize; ++j) { + cur_mask[j] &= tags_[i].mask[j]; + } + break; + } + } + } + for (size_t j = 0; j < maskSize; ++j) { + mask[j] |= cur_mask[j]; + } + } + return mask; +} +uint64 ProgramData::downloadSize() { + auto mask = downloadMask(); + uint64 size = 0; + for (size_t i = 0; i < file_sizes_.size(); ++i) { + if (mask[i / 8] & (1 << (7 - (i & 7)))) { + size += file_sizes_[i]; + } + } + return size; +} + +void ProgramData::download(std::string const& path) { + class DownloadTask : public Task { + public: + DownloadTask(std::string const& path) + : path_(path) + {} + private: + std::string path_; + uint32 run() { + auto& progress = data_->progress_; + progress.reset(new Progress); + auto& ngdp = data_->ngdp_; + + try { + NGDP::CascStorage storage(path_ / "Data"); + NGDP::DataStorage data(storage); + NGDP::Hash hash; + + // save configs + + storage.addConfig(ngdp->version()->cdn, ngdp->load(ngdp->version()->cdn)); + storage.addConfig(data_->selected_build, ngdp->load(data_->selected_build)); + + // save encoding file + + auto buildConfig = data_->build_configs[data_->selected_build]; + std::string encodingHash = split(buildConfig["encoding"])[1]; + NGDP::from_string(hash, encodingHash); + data.addFile(hash, loadData(encodingHash)); + + auto& encoding = data_->encoding_; + + // write .build.info + + File info(path_ / ".build.info", File::REWRITE); + info.printf("Branch!STRING:0|Active!DEC:1|Build Key!HEX:16|CDN Key!HEX:16"); + info.printf("|Install Key!HEX:16|IM Size!DEC:4|CDN Path!STRING:0"); + info.printf("|CDN Hosts!STRING:0|Tags!STRING:0|Armadillo!STRING:0"); + info.printf("|Last Activated!STRING:0|Version!STRING:0|Keyring!HEX:16|KeyService!STRING:0\n"); + + NGDP::from_string(hash, buildConfig["install"]); + auto const* enc = encoding->getEncoding(hash); + auto const* cdnData = ngdp->cdn(); + info.printf("%s|1|%s|%s", ngdp->region().c_str(), data_->selected_build.c_str(), ngdp->version()->cdn.c_str()); + info.printf("|%s|%u|%s", NGDP::to_string(enc->keys[0]).c_str(), enc->usize, cdnData ? cdnData->path.c_str() : ""); + info.printf("|%s|%s|", cdnData ? join(cdnData->hosts, " ").c_str() : "", join(data_->used_tags, ":").c_str()); + + __time64_t curtime; + tm timestruct; + char timebuf[128]; + _time64(&curtime); + _gmtime64_s(×truct, &curtime); + strftime(timebuf, sizeof timebuf, "%Y-%m-%dT%H:%M:%SZ", ×truct); + std::vector version; + std::string build_id; + for (auto const& p : split_multiple(buildConfig["build-name"], "_- ")) { + if (!p.empty() && std::isdigit((unsigned char) p[0])) { + std::string d; + for (size_t i = 0; i < p.size() && std::isdigit((unsigned char)p[i]); ++i) { + d.push_back(p[i]); + } + if (d.size() >= 4) { + build_id = d; + } else { + version.push_back(d); + } + } + } + info.printf("|%s|%s||\n", timebuf, (join(version, ".") + "." + build_id).c_str()); + + // load indices + + loadIndices(storage); + + log("Downloading files..."); + + // fetch download file + + NGDP::from_string(hash, buildConfig["download"]); + enc = encoding->getEncoding(hash); + File download = NGDP::DecodeBLTE(data.addFile(enc->keys[0], loadData(enc->keys[0]))); + if (download.read16(true) != 'DL') { + throw Exception("invalid download file"); + } + if (terminate_) return 1; + download.seek(3, SEEK_CUR); + uint32 numEntries = download.read32(true); + uint32 numTags = download.read16(true); + uint32 filesPos = download.tell(); + download.seek(26 * numEntries, SEEK_CUR); + data_->tags_ = ProgramData::loadTags(download, numTags, numEntries); + auto mask = data_->downloadMask(); + + // calculate size + + progress->filesTotal = 0; + progress->filesDone = 0; + download.seek(filesPos, SEEK_SET); + for (uint32 i = 0; i < numEntries; ++i) { + download.read(hash, sizeof hash); + download.seek(10, SEEK_CUR); + if (!(mask[i / 8] & (1 << (7 - (i & 7))))) continue; + ++progress->filesTotal; + auto const* layout = encoding->getLayout(hash); + if (layout) progress->sizeTotal += layout->csize; + } + notify(0); + + // download + + uint32 failedFiles = 0; + download.seek(filesPos, SEEK_SET); + for (uint32 i = 0; i < numEntries && !terminate_; ++i) { + download.read(hash, sizeof hash); + download.seek(10, SEEK_CUR); + if (!(mask[i / 8] & (1 << (7 - (i & 7))))) continue; + + File file = loadFile(hash); + if (file) { + data.addFile(hash, file); + } else { + ++failedFiles; + } + + auto const* layout = encoding->getLayout(hash); + if (layout) progress->sizeDone += layout->csize; + ++progress->filesDone; + notify(0); + } + if (terminate_) return 1; + + if (failedFiles) { + log(fmtstring("Failed to download %u files", failedFiles)); + } + + log("Installing files..."); + + // fetch install file + + NGDP::from_string(hash, buildConfig["install"]); + enc = encoding->getEncoding(hash); + File install = NGDP::DecodeBLTE(data.addFile(enc->keys[0], loadData(enc->keys[0]))); + if (install.read16(true) != 'IN') { + throw Exception("invalid install file"); + } + if (terminate_) return 1; + install.seek(2, SEEK_CUR); + numTags = install.read16(true); + numEntries = install.read32(true); + data_->tags_ = ProgramData::loadTags(install, numTags, numEntries); + mask = data_->downloadMask(); + + progress->filesTotal = 0; + progress->filesDone = 0; + progress->sizeTotal = 0; + progress->sizeDone = 0; + // calculate size + uint32 installPos = install.tell(); + for (uint32 i = 0; i < numEntries; ++i) { + std::string name; + while (char c = install.getc()) { + name.push_back(c); + } + install.read(hash, sizeof hash); + uint32 usize = install.read32(true); + if (!(mask[i / 8] & (1 << (7 - (i & 7))))) continue; + ++progress->filesTotal; + progress->sizeTotal += usize; + } + notify(0); + + // install + + install.seek(installPos, SEEK_SET); + for (uint32 i = 0; i < numEntries && !terminate_; ++i) { + std::string name; + while (char c = install.getc()) { + name.push_back(c); + } + install.read(hash, sizeof hash); + uint32 usize = install.read32(true); + if (!(mask[i / 8] & (1 << (7 - (i & 7))))) continue; + + enc = encoding->getEncoding(hash); + if (!enc) { + log("Failed to extract " + name); + continue; + } + File file = loadFile(enc->keys[0]); + if (!file) { + log("Failed to extract " + name); + continue; + } + File(path_ / name, File::REWRITE).copy(NGDP::DecodeBLTE(file, usize)); + + ++progress->filesDone; + progress->sizeDone += usize; + notify(0); + } + log("Done"); + } catch (Exception& ex) { + log(fmtstring("Error: %s", ex.what())); + } + + return 1; + } + + void log(std::string const& text) { + data_->progress_->log.push_back(text); + notify(0); + } + + struct IndexEntry { + uint16 index; + uint32 size; + uint32 offset; + }; + struct ArchiveInfo { + std::string name; + std::vector mask; + }; + enum { BlockSize = (1 << 20) }; + std::vector archives_; + std::unordered_map index_; + + void loadIndices(NGDP::CascStorage& storage) { + NGDP::Hash nilHash; + memset(nilHash, 0, sizeof nilHash); + + std::vector archives = split(data_->cdn_config["archives"]); + auto& progress = data_->progress_; + progress->filesTotal = archives.size(); + log("Loading indices..."); + + archives_.resize(archives.size()); + for (size_t i = 0; i < archives.size() && !terminate_; ++i) { + archives_[i].name = archives[i]; + + File index = NGDP::CascStorage::getCache(archives[i] + ".index"); + if (!index) { + File src = data_->ngdp_->load(archives[i], "data", true); + if (!src) continue; + index = NGDP::CascStorage::addCache(archives[i] + ".index", src); + storage.addIndex(archives[i], src); + } + progress->filesDone = i; + notify(0); + + size_t size = index.size(); + for (size_t block = 0; block + 4096 <= size; block += 4096) { + NGDP::Hash hash; + index.seek(block, SEEK_SET); + for (size_t pos = 0; pos + 24 <= 4096; pos += 24) { + index.read(hash, sizeof hash); + if (!memcmp(hash, nilHash, sizeof hash)) { + block = size; + break; + } + auto& dst = index_[NGDP::Hash_container::from(hash)]; + dst.index = i; + dst.size = index.read32(true); + dst.offset = index.read32(true); + } + } + + File mask = NGDP::CascStorage::getCache(archives[i] + ".mask"); + if (!mask) { + File raw = NGDP::CascStorage::getCache(archives[i]); + if (raw) { + uint32 chunks = (raw.size() + BlockSize - 1) / BlockSize; + archives_[i].mask.assign((chunks + 7) / 8, 0xFF); + continue; + } + } else { + archives_[i].mask.resize(mask.size()); + mask.read(&archives_[i].mask[0], archives_[i].mask.size()); + } + } + } + + File loadFile(NGDP::Hash const& hash) { + auto it = index_.find(NGDP::Hash_container::from(hash)); + if (it == index_.end()) return loadData(hash); + + uint32 offset = it->second.offset; + uint32 size = it->second.size; + uint32 blockStart = offset / BlockSize; + uint32 blockEnd = (offset + size + BlockSize - 1) / BlockSize; + auto& mask = archives_[it->second.index].mask; + uint32 getStart = blockEnd; + uint32 getEnd = blockStart; + for (uint32 i = blockStart; i < blockEnd; ++i) { + if (i / 8 >= mask.size() || !(mask[i / 8] & (1 << (i & 7)))) { + getStart = std::min(getStart, i); + getEnd = std::max(getEnd, i + 1); + } + } + if (getStart < getEnd) { + std::string url = data_->ngdp_->geturl(archives_[it->second.index].name, "data"); + HttpRequest request(url); + request.addHeader(fmtstring("Range: bytes=%u-%u", getStart * BlockSize, getEnd * BlockSize - 1)); + request.send(); + uint32 status = request.status(); + if (status != 200 && status != 206) return File(); + auto headers = request.headers(); + File result = request.response(); + File archive = NGDP::CascStorage::addCache(archives_[it->second.index].name); + if (headers.count("Content-Range")) { + uint32 start, end, total; + if (sscanf(headers["Content-Range"].c_str(), "bytes %u-%u/%u", &start, &end, &total) != 3) { + return File(); + } + if (archive.size() < total) { + archive.seek(total - 1, SEEK_SET); + archive.putc(0); + } + archive.seek(start, SEEK_SET); + archive.copy(result, end - start + 1); + start = (start + BlockSize - 1) / BlockSize; + if (end >= total - 1) { + end = (end + BlockSize) / BlockSize; + } else { + end = (end + 1) / BlockSize; + } + total = (total + BlockSize - 1) / BlockSize; + mask.resize((total + 7) / 8, 0); + for (uint32 i = start; i < end; ++i) { + mask[i / 8] |= (1 << (i & 7)); + } + File file = NGDP::CascStorage::addCache(archives_[it->second.index].name + ".mask"); + file.write(&mask[0], mask.size()); + } else { + archive.copy(result); + } + } + + File data = NGDP::CascStorage::getCache(archives_[it->second.index].name); + if (!data) return File(); + MemoryFile result; + data.seek(offset, SEEK_SET); + data.read(result.reserve(size), size); + result.seek(0); + return result; + } + }; + + setTask(new DownloadTask(path)); +} diff --git a/src/data.h b/src/data.h new file mode 100644 index 0000000..a2737e9 --- /dev/null +++ b/src/data.h @@ -0,0 +1,126 @@ +#pragma once + +#include +#include "ngdp.h" + +#define WM_TASKDONE (WM_USER+1928) + +class Application; +class Page; + +class ProgramData { + +public: + void stop() { + setTask(nullptr); + } + + enum { LOADING = -123 }; + + uint32 loading_size; + uint32 loading_progress; + + // step 1 + +public: + void setProgram(std::string const& program); + std::shared_ptr ngdp() { + return ngdp_; + } +private: + std::shared_ptr ngdp_; + + // step 2 + +public: + void loadBuilds(); + NGDP::ConfigFile cdn_config; + std::vector builds; + std::map build_configs; + std::string builds_loaded; + std::string selected_build; + + // step 3 + +public: + void loadTags(); + std::vector> tags; + std::vector used_tags; + uint64 downloadSize(); + +private: + struct Tag { + std::string name; + uint16 type; + std::vector mask; + }; + + std::shared_ptr encoding_; + std::vector file_sizes_; + std::vector tags_; + + static std::vector loadTags(File& file, uint32 numTags, uint32 numEntries); + std::vector downloadMask(); + + // step 4 + +public: + struct Progress { + std::vector log; + + uint32 filesTotal = 0; + uint32 filesDone = 0; + uint64 sizeTotal = 0; + uint64 sizeDone = 0; + }; + + void download(std::string const& path); + std::shared_ptr progress() { + return progress_; + } + void stopDownload() { + stop(); + progress_.reset(); + } + +private: + std::shared_ptr progress_; + + // internal stuff + +public: + ProgramData(Application* app) + : app_(app) + {} + +private: + Application* app_; + class Task { + public: + virtual ~Task(); + + void start(ProgramData* data); + void stop(); + + protected: + virtual uint32 run() { return 0; }; + void notify(uint32 value = 0); + ProgramData* data_ = nullptr; + bool terminate_ = false; + + File loadData(std::string const& hash, int idx = 0); + File loadData(const NGDP::Hash hash, int idx = 0) { + return loadData(NGDP::to_string(hash), idx); + } + + private: + int proc(); + HANDLE thread_ = nullptr; + Page* callback_ = nullptr; + uint32 lastNotify_ = 0; + uint32 lastNotifyCode_ = 0; + }; + friend class Task; + std::shared_ptr task_; + void setTask(Task* task); +}; diff --git a/src/frameui/controlframes.cpp b/src/frameui/controlframes.cpp new file mode 100644 index 0000000..7ad78f1 --- /dev/null +++ b/src/frameui/controlframes.cpp @@ -0,0 +1,650 @@ +#define NOMINMAX +#include +#include + +#include "frameui/framewnd.h" +#include "frameui/fontsys.h" + +#include "controlframes.h" + +//////////////////////////////////////// + +LRESULT Scrollable::onMessage(uint32 message, WPARAM wParam, LPARAM lParam) { + SCROLLINFO si; + int step; + switch (message) { + case WM_SIZE: + memset(&si, 0, sizeof si); + si.cbSize = sizeof si; + si.fMask = SIF_PAGE | SIF_DISABLENOSCROLL; + si.nPage = HIWORD(lParam); + SetScrollInfo(hWnd, SB_VERT, &si, TRUE); + scroll(scrollPos); + return 0; + case WM_VSCROLL: + memset(&si, 0, sizeof si); + si.cbSize = sizeof si; + si.fMask = SIF_ALL; + GetScrollInfo(hWnd, SB_VERT, &si); + switch (LOWORD(wParam)) { + case SB_TOP: + si.nPos = si.nMin; + break; + case SB_BOTTOM: + si.nPos = si.nMax; + break; + case SB_LINEUP: + si.nPos -= 16; + break; + case SB_LINEDOWN: + si.nPos += 16; + break; + case SB_PAGEUP: + si.nPos -= si.nPage; + break; + case SB_PAGEDOWN: + si.nPos += si.nPage; + break; + case SB_THUMBTRACK: + si.nPos = si.nTrackPos; + break; + } + scroll(si.nPos); + SetFocus(hWnd); + return 0; + case WM_MOUSEWHEEL: + SystemParametersInfo(SPI_GETWHEELSCROLLLINES, 0, &step, 0); + if (step < 0) step = 3; + scrollAccum += GET_WHEEL_DELTA_WPARAM(wParam) * step * 16; + scroll(scrollPos - scrollAccum / WHEEL_DELTA); + scrollAccum %= WHEEL_DELTA; + return 0; + } + return M_UNHANDLED; +} + +void Scrollable::scroll(int pos) { + SCROLLINFO si; + memset(&si, 0, sizeof si); + si.cbSize = sizeof si; + si.fMask = SIF_RANGE | SIF_PAGE; + GetScrollInfo(hWnd, SB_VERT, &si); + if (pos < si.nMin) pos = si.nMin; + if (pos > si.nMax - si.nPage + 1) pos = si.nMax - si.nPage + 1; + si.fMask = SIF_POS; + if (pos != scrollPos) { + si.nPos = pos; + SetScrollInfo(hWnd, SB_VERT, &si, TRUE); + + int deltaY = scrollPos - pos; + scrollPos = pos; + RECT rc; + GetClientRect(hWnd, &rc); + ScrollWindowEx(hWnd, 0, deltaY, &rc, &rc, NULL, NULL, SW_ERASE | SW_INVALIDATE); + } +} + +//////////////////////////////////////// + +ButtonFrame::ButtonFrame(std::string const& text, Frame* parent, int id, int style) + : WindowFrame(parent) +{ + create("Button", text, WS_CHILD | WS_TABSTOP | style, 0); + setFont(FontSys::getSysFont()); + setId(id); +} +LRESULT ButtonFrame::onMessage(uint32 message, WPARAM wParam, LPARAM lParam) { + if (message == WM_ERASEBKGND) { + if ((GetWindowLong(hWnd, GWL_STYLE) & BS_TYPEMASK) == BS_GROUPBOX) { + HDC hDC = (HDC) wParam; + HBRUSH hBrush = (HBRUSH) GetClassLongPtr(GetParent(hWnd), GCLP_HBRBACKGROUND); + RECT rc; + GetClientRect(hWnd, &rc); + FillRect(hDC, &rc, hBrush); + InvalidateRect(hWnd, NULL, FALSE); + return TRUE; + } + } + return M_UNHANDLED; +} + +/////////////////////////////////////////////////////// + +HotkeyFrame::HotkeyFrame(Frame* parent, int id) + : WindowFrame(parent) +{ + create(HOTKEY_CLASS, "", WS_CHILD | WS_TABSTOP | ES_WANTRETURN, 0); + setFont(FontSys::getSysFont()); + setId(id); +} + +/////////////////////////////////////////////////////// + +LinkFrame::LinkFrame(std::string const& text, Frame* parent, int id) + : WindowFrame(parent) + , _color(0xFF0000) + , _flags(0) + , hFont(NULL) + , uFont(NULL) + , pressed(false) + , hover(false) +{ + if (WNDCLASSEX* wcx = createclass("LinkFrmClass")) { + wcx->hCursor = LoadCursor(NULL, IDC_HAND); + RegisterClassEx(wcx); + } + create(text, WS_CHILD, 0); + setFont(FontSys::getSysFont()); + resetSize(); + setId(id); +} + +void LinkFrame::resetSize() { + HDC hDC = GetDC(hWnd); + SelectObject(hDC, hFont); + SIZE sz; + std::string const& text = getText(); + GetTextExtentPoint32(hDC, text.c_str(), text.length(), &sz); + ReleaseDC(hWnd, hDC); + setSize(sz.cx, sz.cy); +} +void LinkFrame::setColor(uint32 color) { + _color = color; + InvalidateRect(hWnd, NULL, TRUE); +} +void LinkFrame::setFlags(int flags) { + _flags = flags; + InvalidateRect(hWnd, NULL, TRUE); +} + +LRESULT LinkFrame::onMessage(uint32 message, WPARAM wParam, LPARAM lParam) { + switch (message) { + case WM_SETFONT: + hFont = (wParam ? (HFONT) wParam : FontSys::getSysFont()); + uFont = FontSys::changeFlags(FontSys::getFlags(hFont) | FONT_UNDERLINE, hFont); + if (lParam) InvalidateRect(hWnd, NULL, TRUE); + return 0; + case WM_PAINT: + { + PAINTSTRUCT ps; + HDC hDC = BeginPaint(hWnd, &ps); + + POINT pt; + GetCursorPos(&pt); + ScreenToClient(hWnd, &pt); + + SetBkMode(hDC, OPAQUE); + SetBkColor(hDC, GetSysColor(COLOR_BTNFACE)); + SetTextColor(hDC, _color); + if (pressed || hover) SelectObject(hDC, uFont); + else SelectObject(hDC, hFont); + RECT rc = {0, 0, width(), height()}; + std::string const& text = getText(); + DrawText(hDC, text.c_str(), text.length(), &rc, _flags); + + EndPaint(hWnd, &ps); + } + return 0; + case WM_MOUSEMOVE: + { + int x = LOWORD(lParam); + int y = HIWORD(lParam); + if (!hover && x > 0 && y > 0 && x < width() && y < height()) { + hover = true; + TRACKMOUSEEVENT tme; + memset(&tme, 0, sizeof tme); + tme.cbSize = sizeof tme; + tme.dwFlags = TME_LEAVE; + tme.hwndTrack = hWnd; + TrackMouseEvent(&tme); + if (!pressed) InvalidateRect(hWnd, NULL, FALSE); + } + } + return 0; + case WM_MOUSELEAVE: + { + hover = false; + TRACKMOUSEEVENT tme; + memset(&tme, 0, sizeof tme); + tme.cbSize = sizeof tme; + tme.dwFlags = TME_CANCEL | TME_LEAVE; + tme.hwndTrack = hWnd; + TrackMouseEvent(&tme); + if (!pressed) InvalidateRect(hWnd, NULL, TRUE); + } + return 0; + case WM_LBUTTONDOWN: + SetCapture(hWnd); + pressed = true; + return 0; + case WM_LBUTTONUP: + if (pressed) { + ReleaseCapture(); + pressed = false; + if (hover) notify(WM_COMMAND, MAKELONG(id(), BN_CLICKED), (LPARAM) hWnd); + } + return 0; + } + return M_UNHANDLED; +} + +/////////////////////////////////////////////////////// + +LRESULT EditFrame::onMessage(uint32 message, WPARAM wParam, LPARAM lParam) { + if (message == WM_CTLCOLOREDIT || message == WM_CTLCOLORSTATIC) { + HDC hDC = (HDC) wParam; + if ((fgcolor & 0xFF000000) == 0) SetTextColor(hDC, fgcolor); + if ((bgcolor & 0xFF000000) == 0) SetBkColor(hDC, bgcolor); + if (background) return (uint32) background; + return M_UNHANDLED; + } + return M_UNHANDLED; +} +EditFrame::EditFrame(Frame* parent, int id, int style) + : WindowFrame(parent) +{ + bgcolor = 0xFFFFFFFF; + fgcolor = 0xFFFFFFFF; + background = NULL; + create("Edit", "", style | WS_CHILD | WS_TABSTOP, WS_EX_CLIENTEDGE); + setFont(FontSys::getSysFont()); + setId(id); +} +EditFrame::~EditFrame() { + if (background) DeleteObject(background); +} +void EditFrame::setFgColor(uint32 color) { + fgcolor = color & 0xFFFFFF; + invalidate(); +} +void EditFrame::setBgColor(uint32 color) { + bgcolor = color & 0xFFFFFF; + if (background) DeleteObject(background); + background = CreateSolidBrush(color); + invalidate(); +} + +/////////////////////////////////////////////////////// + +void ComboFrame::onMove(void* data) { + if (hWnd) { + uint32 flags = SWP_NOREPOSITION; + HWND hWndInsertAfter = NULL; + if (visible()) { + if (IsWindowVisible(hWnd)) { + flags |= SWP_NOZORDER; + } else { + flags |= SWP_SHOWWINDOW;// | SWP_NOZORDER; + hWndInsertAfter = HWND_TOP;// getParentWindow(); + } + } else { + flags |= SWP_NOZORDER | SWP_NOMOVE | SWP_NOSIZE | SWP_HIDEWINDOW; + } + if (data) { + DeferWindowPos((HDWP)data, hWnd, hWndInsertAfter, left(), top(), width(), boxHeight, flags); + } else { + SetWindowPos(hWnd, hWndInsertAfter, left(), top(), width(), boxHeight, flags); + } + } +} +ComboFrame::ComboFrame(Frame* parent, int id, int style) + : WindowFrame(parent) +{ + boxHeight = 500; + create("ComboBox", "", style | WS_CHILD | WS_TABSTOP | WS_VSCROLL, 0); + setFont(FontSys::getSysFont()); + setId(id); + setHeight(21); +} +void ComboFrame::reset() { + SendMessage(hWnd, CB_RESETCONTENT, 0, 0); +} +int ComboFrame::addString(std::string const& text, int data) { + int id = SendMessage(hWnd, CB_ADDSTRING, 0, (LPARAM) text.c_str()); + if (id != CB_ERR) SendMessage(hWnd, CB_SETITEMDATA, id, data); + return id; +} +void ComboFrame::delString(int pos) { + SendMessage(hWnd, CB_DELETESTRING, pos, 0); +} +int ComboFrame::getItemData(int item) const { + return SendMessage(hWnd, CB_GETITEMDATA, item, 0); +} +void ComboFrame::setItemData(int item, int data) { + SendMessage(hWnd, CB_SETITEMDATA, item, data); +} +int ComboFrame::getCurSel() const { + return SendMessage(hWnd, CB_GETCURSEL, 0, 0); +} +void ComboFrame::setCurSel(int sel) { + SendMessage(hWnd, CB_SETCURSEL, sel, 0); +} + +/////////////////////////////////////////////////////// + +StaticFrame::StaticFrame(Frame* parent, int id, int style, int exStyle) + : WindowFrame(parent) +{ + create("Static", "", style | SS_NOTIFY | WS_CHILD, exStyle); + setFont(FontSys::getSysFont()); + setId(id); +} +StaticFrame::StaticFrame(std::string const& text, Frame* parent, int id, int style, int exStyle) + : WindowFrame(parent) +{ + create("Static", text, style | SS_NOTIFY | WS_CHILD, exStyle); + setFont(FontSys::getSysFont()); + resetSize(); + setId(id); +} +void StaticFrame::setImage(HANDLE image, int type) { + SendMessage(hWnd, STM_SETIMAGE, (WPARAM) type, (LPARAM) image); +} +void StaticFrame::resetSize() { + HDC hDC = GetDC(hWnd); + SelectObject(hDC, getFont()); + SIZE sz; + std::string text = getText(); + GetTextExtentPoint32(hDC, text.c_str(), text.length(), &sz); + ReleaseDC(hWnd, hDC); + setSize(sz.cx, sz.cy); +} + +/////////////////////////////////////////////////////// + +#include "richedit.h" + +struct EditStreamCookie { + std::string const& str; + int pos; + EditStreamCookie(std::string const& str) + : str(str) + , pos(0) + {} +}; +DWORD CALLBACK RichEditFrame::StreamCallback(DWORD_PTR cookie, LPBYTE buff, LONG cb, LONG* pcb) +{ + EditStreamCookie* ck = (EditStreamCookie*) cookie; + *pcb = ck->str.length() - ck->pos; + if (*pcb > cb) *pcb = cb; + if (*pcb) memcpy(buff, ck->str.c_str() + ck->pos, *pcb); + ck->pos += *pcb; + return 0; +} +RichEditFrame::RichEditFrame(Frame* parent, int id, int style) + : WindowFrame(parent) +{ + create(RICHEDIT_CLASS, "", style | WS_CHILD | WS_TABSTOP, WS_EX_CLIENTEDGE); + setFont(FontSys::getSysFont()); + setId(id); +} +void RichEditFrame::setBackgroundColor(uint32 color) { + SendMessage(hWnd, EM_SETBKGNDCOLOR, 0, color); +} +void RichEditFrame::setRichText(std::string const& text) { + EditStreamCookie cookie(text); + EDITSTREAM es; + es.dwCookie = (DWORD_PTR) &cookie; + es.dwError = 0; + es.pfnCallback = StreamCallback; + SendMessage(hWnd, EM_EXLIMITTEXT, 0, (text.length() < 32768 ? 32768 : text.length() + 1)); + SendMessage(hWnd, EM_STREAMIN, SF_RTF, (uint32) &es); +} + +///////////////////////////////// + +SliderFrame::SliderFrame(Frame* parent, int id, int style) + : WindowFrame(parent) +{ + create(TRACKBAR_CLASS, "", style | WS_CHILD | WS_TABSTOP, 0); + setFont(FontSys::getSysFont()); + setId(id); +} + +void SliderFrame::setPos(int pos) { + SendMessage(hWnd, TBM_SETPOS, TRUE, pos); +} +void SliderFrame::setRange(int minValue, int maxValue) { + SendMessage(hWnd, TBM_SETRANGEMIN, TRUE, minValue); + SendMessage(hWnd, TBM_SETRANGEMAX, TRUE, maxValue); +} +void SliderFrame::setLineSize(int size) { + SendMessage(hWnd, TBM_SETLINESIZE, 0, size); +} +void SliderFrame::setPageSize(int size) { + SendMessage(hWnd, TBM_SETPAGESIZE, 0, size); +} +void SliderFrame::setTicFreq(int freq) { + SendMessage(hWnd, TBM_SETTICFREQ, freq, 0); +} +int SliderFrame::getPos() { + return SendMessage(hWnd, TBM_GETPOS, 0, 0); +} + +///////////////////////////////////////// + +ProgressFrame::ProgressFrame(Frame* parent, int id, int style) + : WindowFrame(parent) +{ + create(PROGRESS_CLASS, "", style | WS_CHILD | WS_TABSTOP, 0); + setFont(FontSys::getSysFont()); + setId(id); +} + +void ProgressFrame::setPos(int pos) { + SendMessage(hWnd, PBM_SETPOS, pos, 0); +} +void ProgressFrame::setRange(int minValue, int maxValue) { + SendMessage(hWnd, PBM_SETRANGE32, minValue, maxValue); +} + +///////////////////////////////////////// + +UpDownFrame::UpDownFrame(Frame* parent, int id, int style) + : WindowFrame(parent) +{ + create(UPDOWN_CLASS, "", WS_CHILD | style, 0); + setId(id); +} + +///////////////////////////////////////// + +TabFrame::TabFrame(Frame* parent, int id, int style) + : WindowFrame(parent) +{ + create(WC_TABCONTROL, "", WS_CHILD | style | WS_CLIPSIBLINGS, 0); + setFont(FontSys::getSysFont()); + setId(id); +} +Frame* TabFrame::addTab(std::string const& text, Frame* frame) { + if (!frame) frame = new Frame(this); + size_t pos = tabs.size(); + tabs.push_back(frame); + + TCITEM item; + memset(&item, 0, sizeof item); + item.mask = TCIF_TEXT; + item.pszText = const_cast(&text[0]); + TabCtrl_InsertItem(hWnd, pos, &item); + frame->show(pos == TabCtrl_GetCurSel(hWnd)); + + RECT rc; + GetClientRect(hWnd, &rc); + int prevWidth = rc.right; + int prevHeight = rc.bottom; + TabCtrl_AdjustRect(hWnd, FALSE, &rc); + frame->setPoint(PT_TOPLEFT, rc.left, rc.top); + frame->setPoint(PT_BOTTOMRIGHT, rc.right - prevWidth, rc.bottom - prevHeight); + + return frame; +} +void TabFrame::clear() { + for (Frame* tab : tabs) tab->hide(); + tabs.clear(); + TabCtrl_DeleteAllItems(hWnd); +} + +LRESULT TabFrame::onMessage(uint32 message, WPARAM wParam, LPARAM lParam) { + if (message == WM_NOTIFY) { + NMHDR* hdr = (NMHDR*) lParam; + if (hdr->hwndFrom == hWnd && hdr->code == TCN_SELCHANGE) { + int sel = TabCtrl_GetCurSel(hWnd); + for (size_t i = 0; i < tabs.size(); i++) { + if (i != sel) tabs[i]->hide(); + } + tabs[sel]->show(); + return 0; + } + } + return M_UNHANDLED; +} +void TabFrame::setCurSel(int sel) { + TabCtrl_SetCurSel(hWnd, sel); + for (size_t i = 0; i < tabs.size(); i++) { + if (i != sel) tabs[i]->hide(); + } + tabs[sel]->show(); +} + +//////////////////////////////////// + +LRESULT ColorFrame::onMessage(uint32 message, WPARAM wParam, LPARAM lParam) { + static COLORREF custColors[16] = { + 0x000000, 0x800000, 0x008000, 0x808000, 0x000080, 0x800080, 0x008080, 0xC0C0C0, + 0x808080, 0xFF0000, 0x00FF00, 0xFFFF00, 0x0000FF, 0xFF00FF, 0x00FFFF, 0xFFFFFF, + }; + switch (message) { + case WM_PAINT: + { + PAINTSTRUCT ps; + HDC hDC = BeginPaint(hWnd, &ps); + std::string text = getText(); + if (!text.empty()) { + RECT rc; + GetClientRect(hWnd, &rc); + SetBkColor(hDC, color); + SetTextColor(hDC, color ^ 0xFFFFFF); + SelectObject(hDC, FontSys::getSysFont()); + DrawText(hDC, text.c_str(), text.length(), &rc, DT_SINGLELINE | DT_CENTER | DT_VCENTER | DT_NOPREFIX); + } + EndPaint(hWnd, &ps); + } + break; + case WM_ERASEBKGND: + { + HDC hDC = (HDC) wParam; + RECT rc; + GetClientRect(hWnd, &rc); + SetBkColor(hDC, color); + ExtTextOut(hDC, 0, 0, ETO_OPAQUE, &rc, NULL, 0, NULL); + } + return TRUE; + case WM_LBUTTONDOWN: + SetCapture(hWnd); + break; + case WM_LBUTTONUP: + if (GetCapture() == hWnd) { + ReleaseCapture(); + + CHOOSECOLOR cc; + memset(&cc, 0, sizeof cc); + cc.lStructSize = sizeof cc; + cc.hwndOwner = GetParent(hWnd); + cc.rgbResult = color; + cc.Flags = CC_FULLOPEN | CC_RGBINIT; + cc.lpCustColors = custColors; + if (ChooseColor(&cc)) { + setColor(cc.rgbResult); + notify(WM_COMMAND, MAKELONG(id(), BN_CLICKED), (LPARAM) hWnd); + } + } + break; + default: + return M_UNHANDLED; + } + return 0; +} +ColorFrame::ColorFrame(uint32 clr, Frame* parent, uint32 id) + : WindowFrame(parent) +{ + color = clr; + create("", WS_CHILD, WS_EX_CLIENTEDGE); + setId(id); +} +ColorFrame::~ColorFrame() +{ +} + +//////////////////////////////////// + +TreeViewFrame::TreeViewFrame(Frame* parent, int id, int style) + : WindowFrame(parent) +{ + create(WC_TREEVIEW, "", style | WS_CHILD | WS_VISIBLE, WS_EX_CLIENTEDGE); + setId(id); + setFont(FontSys::getSysFont()); +} +void TreeViewFrame::setItemText(HTREEITEM item, std::string const& text) { + TVITEM tvi; + memset(&tvi, 0, sizeof tvi); + tvi.hItem = item; + tvi.mask = TVIF_TEXT; + tvi.pszText = const_cast(&text[0]); + TreeView_SetItem(hWnd, &tvi); +} +LPARAM TreeViewFrame::getItemData(HTREEITEM item) { + TVITEM tvi; + memset(&tvi, 0, sizeof tvi); + tvi.hItem = item; + tvi.mask = TVIF_PARAM; + TreeView_GetItem(hWnd, &tvi); + return tvi.lParam; +} + +//////////////////////////////////// + +DateTimeFrame::DateTimeFrame(Frame* parent, int id, int style) + : WindowFrame(parent) +{ + create(DATETIMEPICK_CLASS, "", style | WS_CHILD | WS_VISIBLE, 0); + setId(id); + setFont(FontSys::getSysFont()); +} +void DateTimeFrame::setFormat(char const* fmt) +{ + DateTime_SetFormat(hWnd, fmt); +} + +bool DateTimeFrame::isDateSet() const { + SYSTEMTIME st; + return (DateTime_GetSystemtime(hWnd, &st) == GDT_VALID); +} +uint64 DateTimeFrame::getDate() const { + SYSTEMTIME st; + if (DateTime_GetSystemtime(hWnd, &st) != GDT_VALID) return 0; + FILETIME ft; + SystemTimeToFileTime(&st, &ft); + uint64 result = uint64(ft.dwLowDateTime) | (uint64(ft.dwHighDateTime) << 32); + result = result / 10000000ULL - 11644473600ULL; + return result; +} +void DateTimeFrame::setNoDate() { + DateTime_SetSystemtime(hWnd, GDT_NONE, NULL); +} +void DateTimeFrame::setDate(uint64 date) { + date = (date + 11644473600ULL) * 10000000ULL; + FILETIME ft; + ft.dwLowDateTime = uint32(date); + ft.dwHighDateTime = uint32(date >> 32); + SYSTEMTIME st; + FileTimeToSystemTime(&ft, &st); + DateTime_SetSystemtime(hWnd, GDT_VALID, &st); +} + +/////////////////////////////////////// + +StaticFrame* StaticFrame::addTip(Frame* frame, std::string const& text) { + StaticFrame* tip = new StaticFrame(text, frame->getParent()); + tip->setPoint(PT_RIGHT, frame, PT_LEFT, -8, 0); + return tip; +} diff --git a/src/frameui/controlframes.h b/src/frameui/controlframes.h new file mode 100644 index 0000000..a975d89 --- /dev/null +++ b/src/frameui/controlframes.h @@ -0,0 +1,216 @@ +#pragma once + +#include "frameui/frame.h" +#include "frameui/window.h" +#include "frameui/framewnd.h" +#include + +class Scrollable : public WindowFrame { +public: + Scrollable(Frame* parent) + : WindowFrame(parent) + , scrollPos(0) + , scrollAccum(0) + {} + +protected: + int scrollPos; + int scrollAccum; + void scroll(int pos); + LRESULT onMessage(uint32 message, WPARAM wParam, LPARAM lParam) override; +}; + +class ButtonFrame : public WindowFrame { + LRESULT onMessage(uint32 message, WPARAM wParam, LPARAM lParam); +public: + ButtonFrame(std::string const& text, Frame* parent, int id = 0, int style = BS_PUSHBUTTON); + void setCheck(bool check) { + SendMessage(hWnd, BM_SETCHECK, check ? BST_CHECKED : BST_UNCHECKED, 0); + } + bool checked() const { + return SendMessage(hWnd, BM_GETCHECK, 0, 0) == BST_CHECKED; + } +}; +class LinkFrame : public WindowFrame { + HFONT hFont; + HFONT uFont; + uint32 _color; + uint32 _flags; + bool pressed; + bool hover; + LRESULT onMessage(uint32 message, WPARAM wParam, LPARAM lParam); +public: + LinkFrame(std::string const& text, Frame* parent, int id = 0); + + void setColor(uint32 color); + void setFlags(int flags); + uint32 color() const { + return _color; + } + int flags() const { + return _flags; + } + + void resetSize(); +}; + +class HotkeyFrame : public WindowFrame { +public: + HotkeyFrame(Frame* parent, int id = 0); + + int getKey() const { + return SendMessage(hWnd, HKM_GETHOTKEY, 0, 0) & 0xFFFF; + } + void setKey(int key) { + SendMessage(hWnd, HKM_SETHOTKEY, key & 0xFFFF, 0); + } +}; + +class EditFrame : public WindowFrame { + HBRUSH background; + uint32 bgcolor; + uint32 fgcolor; + LRESULT onMessage(uint32 message, WPARAM wParam, LPARAM lParam); +public: + EditFrame(Frame* parent, int id = 0, int style = ES_AUTOHSCROLL); + ~EditFrame(); + void setFgColor(uint32 color); + void setBgColor(uint32 color); +}; + +class ComboFrame : public WindowFrame { + void onMove(void* data); + int boxHeight; +public: + ComboFrame(Frame* parent, int id = 0, int style = CBS_DROPDOWNLIST); + void reset(); + int addString(std::string const& text, int data = 0); + void delString(int pos); + int getItemData(int item) const; + void setItemData(int item, int data); + int getCurSel() const; + void setCurSel(int sel); + int getCount() const { + return SendMessage(hWnd, CB_GETCOUNT, 0, 0); + } + void setBoxHeight(int ht) { + boxHeight = ht; + onMove(0); + } +}; + +class StaticFrame : public WindowFrame { +public: + StaticFrame(Frame* parent, int id = 0, int style = 0, int exStyle = 0); + StaticFrame(std::string const& text, Frame* parent, int id = 0, int style = 0, int exStyle = 0); + void setImage(HANDLE image, int type = IMAGE_BITMAP); + void resetSize(); + + static StaticFrame* addTip(Frame* frame, std::string const& text); +}; + +class RichEditFrame : public WindowFrame { + static DWORD CALLBACK StreamCallback(DWORD_PTR cookie, LPBYTE buff, LONG cb, LONG* pcb); +public: + RichEditFrame(Frame* parent, int id = 0, int style = WS_VSCROLL | WS_HSCROLL | + ES_MULTILINE | ES_AUTOHSCROLL | ES_AUTOVSCROLL | ES_READONLY); + void setBackgroundColor(uint32 color); + void setRichText(std::string const& text); +}; + +class SliderFrame : public WindowFrame { +public: + SliderFrame(Frame* parent, int id = 0, int style = TBS_AUTOTICKS | TBS_BOTH); + + void setPos(int pos); + void setRange(int minValue, int maxValue); + void setLineSize(int size); + void setPageSize(int size); + void setTicFreq(int freq); + int getPos(); +}; +class ProgressFrame : public WindowFrame { +public: + ProgressFrame(Frame* parent, int id = 0, int style = 0); + + void setRange(int minValue, int maxValue); + void setPos(int pos); +}; + +class UpDownFrame : public WindowFrame { +public: + UpDownFrame(Frame* parent, int id = 0, int style = 0); +}; + +class TabFrame : public WindowFrame { +protected: + std::vector tabs; + LRESULT onMessage(uint32 message, WPARAM wParam, LPARAM lParam); +public: + TabFrame(Frame* parent, int id = 0, int style = 0); + + size_t numTabs() const { + return tabs.size(); + } + Frame* addTab(std::string const& text, Frame* frame = NULL); + Frame* getTab(size_t pos) const { + return (pos >= tabs.size() ? nullptr : tabs[pos]); + } + + void clear(); + + int getCurSel() const { + return TabCtrl_GetCurSel(hWnd); + } + void setCurSel(int sel); +}; + +class ColorFrame : public WindowFrame { + uint32 color; + LRESULT onMessage(uint32 message, WPARAM wParam, LPARAM lParam); +public: + ColorFrame(uint32 clr, Frame* parent, uint32 id); + ~ColorFrame(); + + void setColor(uint32 clr) { + color = clr; + invalidate(); + } + uint32 getColor() { + return color; + } +}; + +class TreeViewFrame : public WindowFrame { +public: + TreeViewFrame(Frame* parent, int id = 0, int style = 0); + + void setImageList(HIMAGELIST list, int type) { + TreeView_SetImageList(hWnd, list, type); + } + void setItemHeight(int height) { + TreeView_SetItemHeight(hWnd, height); + } + + HTREEITEM insertItem(TVINSERTSTRUCT* tvis) { + return TreeView_InsertItem(hWnd, tvis); + } + void deleteItem(HTREEITEM item) { + TreeView_DeleteItem(hWnd, item); + } + + LPARAM getItemData(HTREEITEM item); + + void setItemText(HTREEITEM item, std::string const& text); +}; + +class DateTimeFrame : public WindowFrame { +public: + DateTimeFrame(Frame* parent, int id = 0, int style = DTS_SHORTDATEFORMAT); + void setFormat(char const* fmt); + + bool isDateSet() const; + uint64 getDate() const; + void setNoDate(); + void setDate(uint64 date); +}; diff --git a/src/frameui/fontsys.cpp b/src/frameui/fontsys.cpp new file mode 100644 index 0000000..3388bbc --- /dev/null +++ b/src/frameui/fontsys.cpp @@ -0,0 +1,171 @@ +#include "fontsys.h" +#include + +FontSys FontSys::instance; + +//#define MKSIZE(s) (-MulDiv(s,instance.logPixelsY,72)) +#define MKSIZE(s) (-(s)) + +FontSys::FontStruct::FontStruct(LOGFONT const& lf) { + font = CreateFontIndirect(&lf); + face = lf.lfFaceName; + size = lf.lfHeight; + flags = 0; + if (lf.lfWeight > FW_NORMAL) flags |= FONT_BOLD; + if (lf.lfItalic) flags |= FONT_ITALIC; + if (lf.lfUnderline) flags |= FONT_UNDERLINE; + if (lf.lfStrikeOut) flags |= FONT_STRIKEOUT; +} + +bool operator<(FontSys::FontStruct const& lhs, FontSys::FontStruct const& rhs) { + if (lhs.size != rhs.size) return lhs.size < rhs.size; + if (lhs.flags != rhs.flags) return lhs.flags < rhs.flags; + return lhs.face < rhs.face; +} +bool operator==(FontSys::FontStruct const& lhs, FontSys::FontStruct const& rhs) { + if (lhs.size != rhs.size) return false; + if (lhs.flags != rhs.flags) return false; + return lhs.face == rhs.face; +} + +FontSys::FontSys() { + LOGFONT lf; + memset (&lf, 0, sizeof lf); + lf.lfHeight = -11; + lf.lfWeight = FW_NORMAL; + strcpy(lf.lfFaceName, "MS Shell Dlg 2"); + lf.lfCharSet = DEFAULT_CHARSET; + + fonts.emplace_back(lf); + + hDC = CreateCompatibleDC(NULL); + logPixelsY = GetDeviceCaps(hDC, LOGPIXELSY); +} + +FontSys::~FontSys() { + DeleteDC(hDC); +} + +HFONT FontSys::getSysFont() { + return instance.fonts[0].font; +} +HFONT FontSys::_getFont(int height, std::string const& face, int flags) { + FontStruct fs; + fs.size = height; + fs.flags = flags; + fs.face = face; + + auto pos = std::lower_bound(fonts.begin() + 1, fonts.end(), fs); + if (pos != fonts.end() && *pos == fs) { + return pos->font; + } + + LOGFONT lf; + getLogFont(&lf, -height, face, flags); + return fonts.emplace(pos, lf)->font; +} + +void FontSys::getLogFont(LOGFONT* lf, int size, std::string const& face, int flags) { + GetObject(instance.fonts[0].font, sizeof(LOGFONT), lf); + lf->lfHeight = MKSIZE(size); + strcpy(lf->lfFaceName, face.c_str()); + lf->lfWeight = (flags & FONT_BOLD ? FW_BOLD : FW_NORMAL); + lf->lfItalic = (flags & FONT_ITALIC ? TRUE : FALSE); + lf->lfUnderline = (flags & FONT_UNDERLINE ? TRUE : FALSE); + lf->lfStrikeOut = (flags & FONT_STRIKEOUT ? TRUE : FALSE); +} +HFONT FontSys::getFont(int size, std::string const& face, int flags) { + return instance._getFont(MKSIZE(size), face, flags); +} +HFONT FontSys::getFont(int size, int flags) { + return instance._getFont(MKSIZE(size), instance.fonts[0].face, flags); +} +HFONT FontSys::changeSize(int size, HFONT oldFont) { + if (oldFont == NULL) oldFont = getSysFont(); + LOGFONT lf; + GetObject(oldFont, sizeof lf, &lf); + int flags = 0; + if (lf.lfWidth > FW_NORMAL) flags |= FONT_BOLD; + if (lf.lfItalic) flags |= FONT_ITALIC; + if (lf.lfUnderline) flags |= FONT_UNDERLINE; + if (lf.lfStrikeOut) flags |= FONT_STRIKEOUT; + return instance._getFont(MKSIZE(size), lf.lfFaceName, flags); +} +HFONT FontSys::changeFlags(int flags, HFONT oldFont) { + if (oldFont == NULL) oldFont = getSysFont(); + LOGFONT lf; + GetObject(oldFont, sizeof lf, &lf); + return instance._getFont(lf.lfHeight, lf.lfFaceName, flags); +} + +SIZE FontSys::getTextSize(HFONT font, std::string const& text) { + SIZE sz; + SelectObject(instance.hDC, font); + GetTextExtentPoint32(instance.hDC, text.c_str(), text.length(), &sz); + return sz; +} +SIZE FontSys::getTextSize(HFONT font, char const* text, int length) { + SIZE sz; + SelectObject(instance.hDC, font); + GetTextExtentPoint32(instance.hDC, text, length, &sz); + return sz; +} +int FontSys::getMTextHeight(HFONT font, int width, std::string const& text) { + SIZE sz; + SelectObject(instance.hDC, font); + std::string word; + std::string curline; + int height = 0; + for (int i = 0; i <= text.length (); i++) { + if (text[i] && text[i] != '\n' && !isspace((uint8)text[i])) { + word += text[i]; + } + if (text[i] == 0 || isspace((uint8) text[i])) { + std::string tmp = curline + word; + GetTextExtentPoint32(instance.hDC, tmp.c_str(), tmp.length(), &sz); + int wd = sz.cx; + if (text[i] == '\n' || (wd > width && !curline.empty())) { + GetTextExtentPoint32(instance.hDC, curline.c_str(), curline.length(), &sz); + height += sz.cy; + curline.clear(); + } + curline += word; + if (text[i]) curline += text[i]; + word.clear(); + } + } + if (!curline.empty()) { + GetTextExtentPoint32(instance.hDC, curline.c_str(), curline.length(), &sz); + height += sz.cy; + } + return height; +} + +int FontSys::getFlags(HFONT font) { + if (font == NULL) font = getSysFont(); + LOGFONT lf; + GetObject(font, sizeof lf, &lf); + int flags = 0; + if (lf.lfWidth > FW_NORMAL) flags |= FONT_BOLD; + if (lf.lfItalic) flags |= FONT_ITALIC; + if (lf.lfUnderline) flags |= FONT_UNDERLINE; + if (lf.lfStrikeOut) flags |= FONT_STRIKEOUT; + return flags; +} + +void FontSys::setFontName(HFONT hFont, std::string const& name) { + instance.byName[name] = hFont; +} +HFONT FontSys::getFontByName(std::string const& name) { + auto it = instance.byName.find(name); + return (it == instance.byName.end() ? instance.fonts[0].font : it->second); +} + +HFONT FontSys::getFont(LOGFONT const& lf) { + int flags = 0; + if (lf.lfWidth > FW_NORMAL) flags |= FONT_BOLD; + if (lf.lfItalic) flags |= FONT_ITALIC; + if (lf.lfUnderline) flags |= FONT_UNDERLINE; + if (lf.lfStrikeOut) flags |= FONT_STRIKEOUT; + return instance._getFont(lf.lfHeight, lf.lfFaceName, flags); +} diff --git a/src/frameui/fontsys.h b/src/frameui/fontsys.h new file mode 100644 index 0000000..75ad50d --- /dev/null +++ b/src/frameui/fontsys.h @@ -0,0 +1,64 @@ +#pragma once + +#include +#include +#include +#include +#include "base/types.h" + +#define FONT_BOLD 0x0001 +#define FONT_ITALIC 0x0002 +#define FONT_UNDERLINE 0x0004 +#define FONT_STRIKEOUT 0x0008 + +class FontSys { + struct FontStruct { + HFONT font; + std::string face; + int size; + int flags; + FontStruct() : font(nullptr) {} + FontStruct(LOGFONT const& lf); + FontStruct(FontStruct&& fs) + : font(fs.font) + , face(std::move(fs.face)) + , size(fs.size) + , flags(fs.flags) + { + fs.font = nullptr; + } + FontStruct(FontStruct const& fs) = delete; + ~FontStruct() { + if (font) DeleteObject(font); + } + }; + friend bool operator<(FontSys::FontStruct const& lhs, FontSys::FontStruct const& rhs); + friend bool operator==(FontSys::FontStruct const& lhs, FontSys::FontStruct const& rhs); + std::vector fonts; + int logPixelsY; + HDC hDC; + FontSys(); + std::map byName; + static FontSys instance; + HFONT _getFont(int height, std::string const& face, int flags = 0); +public: + ~FontSys(); + + static HFONT getSysFont(); + static HFONT getFont(int size, std::string const& face, int flags = 0); + static HFONT getFont(int size, int flags = 0); + static HFONT changeSize(int size, HFONT oldFont = NULL); + static HFONT changeFlags(int flags, HFONT oldFont = NULL); + static void setFontName(HFONT hFont, std::string const& name); + static HFONT getFontByName(std::string const& name); + + static HFONT getFont(LOGFONT const& lf); + + static void getLogFont(LOGFONT* lf, int size, std::string const& face, int flags = 0); + + static int getFlags(HFONT font = NULL); + + static SIZE getTextSize(HFONT font, std::string const& text); + static SIZE getTextSize(HFONT font, char const* text, int length); + static int getMTextHeight(HFONT font, int width, std::string const& text); +}; diff --git a/src/frameui/frame.cpp b/src/frameui/frame.cpp new file mode 100644 index 0000000..09e3ea0 --- /dev/null +++ b/src/frameui/frame.cpp @@ -0,0 +1,436 @@ +#include +#include +#include "frame.h" + +RootFrame::RootFrame() + : Frame(NULL) +{ + frames.push_back(this); + master = this; + frames[0]->master = this; + frames[0]->mr_pos = 0; + moveid = 0; + updating = false; + frames[0]->mr_valid = true; + frames[0]->_x = 0; + frames[0]->_y = 0; + frames[0]->_width = 0; + frames[0]->_height = 0; +} +RootFrame::~RootFrame() { + frames[0]->master = NULL; + frames[0]->firstChild = NULL; + for (size_t i = 1; i < frames.size(); i++) { + frames[i]->master = NULL; + frames[i]->parent = NULL; + frames[i]->prevSibling = NULL; + frames[i]->nextSibling = NULL; + frames[i]->firstChild = NULL; + delete frames[i]; + } +} +void RootFrame::shutdown() { + for (Frame* frame : frames) { + frame->onMessage(FM_SHUTDOWN, 0, 0); + } +} +void RootFrame::setSize(int width, int height) { + frames[0]->_width = width; + frames[0]->_height = height; + deepUpdateFrame(frames[0]); +} +void RootFrame::addFrame(Frame* r) { + r->mr_pos = frames.size(); + frames.push_back(r); +} +void RootFrame::removeFrame(Frame* r) { + for (size_t i = r->mr_pos + 1; i < frames.size(); ++i) { + for (int j = 0; j < Frame::Anchor::count; ++j) { + if (frames[i]->anchors[j].rel == r) { + frames[i]->anchors[j].active = false; + } + } + frames[i]->mr_pos = i - 1; + frames[i - 1] = frames[i]; + } + frames.pop_back(); +} + +void RootFrame::setPoint(Frame* r, int point, Frame* rel, int relPoint, int x, int y) { + uint32 xRel = 0; + if ((relPoint & 0x0F) == 0x01) { // hCenter + xRel = 0x4000; + } else if ((relPoint & 0x0F) == 0x02) { // hRight + xRel = 0x8000; + } + uint32 yRel = 0; + if ((relPoint & 0xF0) == 0x10) { // vCenter + yRel = 0x4000; + } else if ((relPoint & 0xF0) == 0x20) { // vBottom + yRel = 0x8000; + } + setPointEx(r, point, rel, xRel, yRel, x, y); +} +void RootFrame::setPointEx(Frame* r, int point, Frame* rel, uint32 xRel, uint32 yRel, int x, int y) { + if (rel == NULL) rel = frames[0]; + // move the tree + if (!updating) moveid++; + int cur = r->mr_pos; + int target = rel->mr_pos; + if (cur < target && updating) return; + r->mr_moving = moveid; + int count = 1; + if (cur < target) { + std::vector temp; + temp.push_back(r); + while (++cur <= target) { + for (int i = 0; i < Frame::Anchor::count; i++) { + if (frames[cur]->anchors[i].rel && + frames[cur]->anchors[i].rel->mr_moving == moveid) + { + frames[cur]->mr_moving = moveid; + temp.push_back(frames[cur]); + break; + } + } + if (cur == target && frames[cur]->mr_moving == moveid) return; + } + count = 0; + for (cur = r->mr_pos; cur <= target; cur++) { + if (frames[cur]->mr_moving == moveid) { + count++; + } else { + frames[cur]->mr_pos = cur - count; + frames[cur - count] = frames[cur]; + } + } + for (size_t i = 0; i < temp.size(); i++) { + temp[i]->mr_pos = target - count + i + 1; + frames[target - count + i + 1] = temp[i]; + } + } else { + target = cur; + } + + // add the anchor + if ((point & 0x0F) == 0x00) { // hLeft + r->anchors[Frame::Anchor::hCenter].active = false; + if (r->anchors[Frame::Anchor::hRight].active) { + r->anchors[Frame::Anchor::hWidth].active = false; + } + Frame::Anchor& a = r->anchors[Frame::Anchor::hLeft]; + a.active = true; + a.rel = rel; + a.relRatio = xRel; + a.offset = x; + } else if ((point & 0x0F) == 0x01) { // hCenter + if (r->anchors[Frame::Anchor::hLeft].active == false && + r->anchors[Frame::Anchor::hRight].active == false) + { + Frame::Anchor& a = r->anchors[Frame::Anchor::hCenter]; + a.active = true; + a.rel = rel; + a.relRatio = xRel; + a.offset = x; + } + } else { // hRight + r->anchors[Frame::Anchor::hCenter].active = false; + if (r->anchors[Frame::Anchor::hLeft].active) { + r->anchors[Frame::Anchor::hWidth].active = false; + } + Frame::Anchor& a = r->anchors[Frame::Anchor::hRight]; + a.active = true; + a.rel = rel; + a.relRatio = xRel; + a.offset = x; + } + if ((point & 0xF0) == 0x00) { // vTop + r->anchors[Frame::Anchor::vCenter].active = false; + if (r->anchors[Frame::Anchor::vBottom].active) { + r->anchors[Frame::Anchor::vHeight].active = false; + } + Frame::Anchor& a = r->anchors[Frame::Anchor::vTop]; + a.active = true; + a.rel = rel; + a.relRatio = yRel; + a.offset = y; + } else if ((point & 0xF0) == 0x10) { // vCenter + if (r->anchors[Frame::Anchor::vTop].active == false && + r->anchors[Frame::Anchor::vBottom].active == false) + { + Frame::Anchor& a = r->anchors[Frame::Anchor::vCenter]; + a.active = true; + a.rel = rel; + a.relRatio = yRel; + a.offset = y; + } + } else { // vBottom + r->anchors[Frame::Anchor::vCenter].active = false; + if (r->anchors[Frame::Anchor::vTop].active) { + r->anchors[Frame::Anchor::vHeight].active = false; + } + Frame::Anchor& a = r->anchors[Frame::Anchor::vBottom]; + a.active = true; + a.rel = rel; + a.relRatio = yRel; + a.offset = y; + } + // update positions + if (!updating) { + updating = true; + void* moveData = beginMoving(); + for (size_t cur = target - count + 1; cur < frames.size(); cur++) { + for (int i = 0; i < Frame::Anchor::count && frames[cur]->mr_moving != moveid; i++) { + if (frames[cur]->anchors[i].rel && + frames[cur]->anchors[i].rel->mr_moving == moveid) { + frames[cur]->mr_moving = moveid; + } + } + if (frames[cur]->mr_moving == moveid) updateFrame(frames[cur], moveData); + } + endMoving(moveData); + updating = false; + } +} +void RootFrame::setWidth(Frame* r, int width) { + if (r->anchors[Frame::Anchor::hLeft].active && + r->anchors[Frame::Anchor::hRight].active) { + return; + } + Frame::Anchor& c = r->anchors[Frame::Anchor::hWidth]; + c.active = true; + c.offset = width; + deepUpdateFrame(r); +} +void RootFrame::setHeight(Frame* r, int height) { + if (r->anchors[Frame::Anchor::vTop].active && + r->anchors[Frame::Anchor::vBottom].active) { + return; + } + Frame::Anchor& c = r->anchors[Frame::Anchor::vHeight]; + c.active = true; + c.offset = height; + deepUpdateFrame(r); +} +void RootFrame::deepUpdateFrame(Frame* r) { + if (!updating) { + updating = true; + void* moveData = beginMoving(); + moveid++; + r->mr_moving = moveid; + for (size_t cur = r->mr_pos; cur < frames.size(); cur++) { + for (int i = 0; i < Frame::Anchor::count && frames[cur]->mr_moving != moveid; i++) { + if (frames[cur]->anchors[i].rel && + frames[cur]->anchors[i].rel->mr_moving == moveid) { + frames[cur]->mr_moving = moveid; + } + } + if (frames[cur]->mr_moving == moveid) updateFrame(frames[cur], moveData); + } + endMoving(moveData); + updating = false; + } +} +inline int getRegionX(Frame* r, uint32 ratio) { + return r->left() + ((r->width() * ratio) >> 15); +} +inline int getRegionY(Frame* r, uint32 ratio) { + return r->top() + ((r->height() * ratio) >> 15); +} +void RootFrame::updateFrame(Frame* r, void* data) { + int oldLeft = r->left(); + int oldTop = r->top(); + int oldRight = r->right(); + int oldBottom = r->bottom(); + if (r->anchors[Frame::Anchor::hLeft].active && + r->anchors[Frame::Anchor::hRight].active) + { + Frame::Anchor& la = r->anchors[Frame::Anchor::hLeft]; + Frame::Anchor& ra = r->anchors[Frame::Anchor::hRight]; + if (la.rel && la.rel->mr_valid && ra.rel && ra.rel->mr_valid) { + r->_x = getRegionX(la.rel, la.relRatio) + la.offset; + r->_width = getRegionX(ra.rel, ra.relRatio) + ra.offset - r->_x; + } else { + if (r->mr_valid) { + r->mr_valid = false; + r->onChangeVisibility(data); + } + return; + } + } else if (r->anchors[Frame::Anchor::hWidth].active) { + r->_width = r->anchors[Frame::Anchor::hWidth].offset; + Frame::Anchor& la = r->anchors[Frame::Anchor::hLeft]; + Frame::Anchor& ra = r->anchors[Frame::Anchor::hRight]; + Frame::Anchor& ca = r->anchors[Frame::Anchor::hCenter]; + if (la.active && la.rel && la.rel->mr_valid) { + r->_x = getRegionX(la.rel, la.relRatio) + la.offset; + } else if (ra.active && ra.rel && ra.rel->mr_valid) { + r->_x = getRegionX(ra.rel, ra.relRatio) + ra.offset - r->_width; + } else if (ca.active && ca.rel && ca.rel->mr_valid) { + r->_x = getRegionX(ca.rel, ca.relRatio) + ca.offset - r->_width / 2; + } else { + if (r->mr_valid) { + r->mr_valid = false; + r->onChangeVisibility(data); + } + return; + } + } + if (r->anchors[Frame::Anchor::vTop].active && + r->anchors[Frame::Anchor::vBottom].active) + { + Frame::Anchor& ta = r->anchors[Frame::Anchor::vTop]; + Frame::Anchor& ba = r->anchors[Frame::Anchor::vBottom]; + if (ta.rel && ta.rel->mr_valid && ba.rel && ba.rel->mr_valid) { + r->_y = getRegionY(ta.rel, ta.relRatio) + ta.offset; + r->_height = getRegionY(ba.rel, ba.relRatio) + ba.offset - r->_y; + } else { + if (r->mr_valid) { + r->mr_valid = false; + r->onChangeVisibility(data); + } + return; + } + } else if (r->anchors[Frame::Anchor::vHeight].active) { + r->_height = r->anchors[Frame::Anchor::vHeight].offset; + Frame::Anchor& ta = r->anchors[Frame::Anchor::vTop]; + Frame::Anchor& ba = r->anchors[Frame::Anchor::vBottom]; + Frame::Anchor& ca = r->anchors[Frame::Anchor::vCenter]; + if (ta.active && ta.rel && ta.rel->mr_valid) { + r->_y = getRegionY(ta.rel, ta.relRatio) + ta.offset; + } else if (ba.active && ba.rel && ba.rel->mr_valid) { + r->_y = getRegionY(ba.rel, ba.relRatio) + ba.offset - r->_height; + } else if (ca.active && ca.rel && ca.rel->mr_valid) { + r->_y = getRegionY(ca.rel, ca.relRatio) + ca.offset - r->_height / 2; + } else { + if (r->mr_valid) { + r->mr_valid = false; + r->onChangeVisibility(data); + } + return; + } + } + if (r->left() != oldLeft || r->top() != oldTop || + r->right() != oldRight || r->bottom() != oldBottom || + !r->mr_valid) + { + if (!r->mr_valid) { + r->mr_valid = true; + r->onChangeVisibility(data); + } else { + r->onMove(data); + } + } +} + +//////////////////////////// + +Frame::Frame(Frame* _parent) + : parent(_parent) + , master(nullptr) + , mr_pos(0) + , mr_moving(0) + , mr_valid(false) + , _x(0) + , _y(0) + , _width(0) + , _height(0) + , _visible(true) +{ + memset(anchors, 0, sizeof anchors); + if (parent) master = parent->master; + if (master) master->addFrame(this); + + firstChild = NULL; + lastChild = NULL; + if (parent) { + prevSibling = parent->lastChild; + if (parent->lastChild) parent->lastChild->nextSibling = this; + else parent->firstChild = this; + parent->lastChild = this; + } else { + prevSibling = NULL; + } + nextSibling = NULL; +} +Frame::~Frame() { + setParent(NULL); + Frame* cur = firstChild; + while (cur) { + Frame* next = cur->nextSibling; + cur->parent = NULL; + cur->prevSibling = NULL; + cur->nextSibling = NULL; + delete cur; + cur = next; + } + if (master) master->removeFrame(this); +} + +void Frame::setParent(Frame* _parent) { + if (parent == _parent) return; + if (parent) { + if (prevSibling) prevSibling->nextSibling = nextSibling; + else parent->firstChild = nextSibling; + if (nextSibling) nextSibling->prevSibling = prevSibling; + else parent->lastChild = prevSibling; + prevSibling = NULL; + nextSibling = NULL; + } + parent = _parent; + if (parent) { + prevSibling = parent->lastChild; + nextSibling = NULL; + if (parent->lastChild) parent->lastChild->nextSibling = this; + else parent->firstChild = this; + parent->lastChild = this; + } +} + +void Frame::onChangeVisibility(void* data) { + onMove(data); + for (Frame* cur = firstChild; cur; cur = cur->nextSibling) { + if (cur->_visible) cur->onChangeVisibility(data); + } +} +void Frame::show(bool s) { + if (s != _visible) { + _visible = s; + void* moveData = master->beginMoving(); + onChangeVisibility(moveData); + master->endMoving(moveData); + } +} + +LRESULT Frame::notify(uint32 message, WPARAM wParam, LPARAM lParam) { + Frame* cur = this; + uint32 result = M_UNHANDLED; + while (cur && (result = cur->onMessage(message, wParam, lParam)) == M_UNHANDLED) { + cur = cur->getParent(); + if (cur == this) break; + } + return result; +} + +void Frame::setWidth(int width) { + master->setWidth(this, width); +} +void Frame::setHeight(int height) { + master->setHeight(this, height); +} +void Frame::setPoint(int point, Frame* rel, int relPoint, int x, int y) { + master->setPoint(this, point, rel, relPoint, x, y); +} +void Frame::setPointEx(int point, Frame* rel, double xRel, double yRel, int x, int y) { + master->setPointEx(this, point, rel, uint32(xRel * 0x8000), uint32(yRel * 0x8000), x, y); +} +void Frame::clearAllPoints() { + for (int i = 0; i < Anchor::count; i++) { + anchors[i].active = false; + } + anchors[Anchor::hWidth].active = true; + anchors[Anchor::vHeight].active = true; + master->deepUpdateFrame(this); +} + +//////////////////////////////////// diff --git a/src/frameui/frame.h b/src/frameui/frame.h new file mode 100644 index 0000000..3473444 --- /dev/null +++ b/src/frameui/frame.h @@ -0,0 +1,158 @@ +#pragma once + +#include +#include +#include "base/types.h" + +#define PT_TOPLEFT 0x00 +#define PT_TOP 0x01 +#define PT_TOPRIGHT 0x02 +#define PT_LEFT 0x10 +#define PT_CENTER 0x11 +#define PT_RIGHT 0x12 +#define PT_BOTTOMLEFT 0x20 +#define PT_BOTTOM 0x21 +#define PT_BOTTOMRIGHT 0x22 + +#define M_UNHANDLED 0x80000000 +#define FM_SHUTDOWN 0x7F000001 + +class Frame { + // master frame data + friend class RootFrame; + int mr_pos; + int mr_moving; + bool mr_valid; + + bool _visible; + int _x; + int _y; + int _width; + int _height; + struct Anchor { + enum {hLeft, hCenter, hRight, hWidth, vTop, vCenter, vBottom, vHeight, count}; + bool active; + Frame* rel; + uint32 relRatio; + int offset; + } anchors[8]; + + RootFrame* master; + Frame* parent; + Frame* firstChild; + Frame* lastChild; + Frame* prevSibling; + Frame* nextSibling; + + void onChangeVisibility(void* data); +protected: + virtual void onMove(void* data) {} +public: + Frame(Frame* parent); + virtual ~Frame(); + + // hierarchy + void setParent(Frame* parent); + Frame* getParent() const { + return parent; + } + Frame* getFirstChild() const { + return firstChild; + } + Frame* getLastChild() const { + return lastChild; + } + Frame* getPrevSibling() const { + return prevSibling; + } + Frame* getNextSibling() const { + return nextSibling; + } + + // visibility + void show(bool s = true); + void hide() { + show(false); + } + bool visible() const { + return _visible && mr_valid && (parent ? parent->visible() : true); + } + + // positioning + int left() const { + return _x; + } + int top() const { + return _y; + } + int right() const { + return _x + _width; + } + int bottom() const { + return _y + _height; + } + int width() const { + return _width; + } + int height() const { + return _height; + } + void setWidth(int width); + void setHeight(int height); + void setSize(int width, int height) { + setWidth(width); + setHeight(height); + } + void setPoint(int point, Frame* rel, int relPoint, int x, int y); + void setPoint(int point, Frame* rel, int x, int y) { + setPoint(point, rel, point, x, y); + } + void setPoint(int point, int x, int y) { + setPoint(point, parent, point, x, y); + } + void setPointEx(int point, Frame* rel, double xRel, double yRel, int x, int y); + void setPointEx(int point, double xRel, double yRel, int x, int y) { + setPointEx(point, parent, xRel, yRel, x, y); + } + void clearAllPoints(); + void setAllPoints(Frame* rel) { + setPoint(PT_TOPLEFT, rel, PT_TOPLEFT, 0, 0); + setPoint(PT_BOTTOMRIGHT, rel, PT_BOTTOMRIGHT, 0, 0); + } + void setAllPoints() { + setPoint(PT_TOPLEFT, parent, PT_TOPLEFT, 0, 0); + setPoint(PT_BOTTOMRIGHT, parent, PT_BOTTOMRIGHT, 0, 0); + } + + // M_UNHANDLED = unprocessed + virtual LRESULT onMessage(uint32 message, WPARAM wParam, LPARAM lParam) { + return M_UNHANDLED; + } + LRESULT notify(uint32 message, WPARAM wParam, LPARAM lParam); +}; + +class RootFrame : public Frame { + friend class Frame; + + std::vector frames; + int moveid; + bool updating; + + void addFrame(Frame* r); + void updateFrame(Frame* r, void* data); + void deepUpdateFrame(Frame* r); + void setPoint(Frame* r, int point, Frame* rel, int relPoint, int x, int y); + void setPointEx(Frame* r, int point, Frame* rel, uint32 xRel, uint32 yRel, int x, int y); + void setWidth(Frame* r, int width); + void setHeight(Frame* r, int height); + void removeFrame(Frame* r); +protected: + virtual void* beginMoving() { return 0; } + virtual void endMoving(void* data) {} + void shutdown(); +public: + RootFrame(); + ~RootFrame(); + + void setSize(int width, int height); +}; diff --git a/src/frameui/framewnd.cpp b/src/frameui/framewnd.cpp new file mode 100644 index 0000000..5ca48fe --- /dev/null +++ b/src/frameui/framewnd.cpp @@ -0,0 +1,209 @@ +#include + +#include "framewnd.h" + +WindowFrame::WindowFrame(Frame* parent) + : Frame(parent) +{ + Frame* cur = getParent(); + while (cur->getParent()) cur = cur->getParent(); + RootWindow* frm = dynamic_cast(cur); + if (frm) ownerWindow = frm->getHandle(); + else ownerWindow = nullptr; +} + +HWND WindowFrame::getParentWindow() const { + return HWND_TOP; + Frame* cur = getParent(); + WindowFrame* curWnd = nullptr; + while (cur && !(curWnd = dynamic_cast(cur))) { + cur = cur->getParent(); + } + if (cur && curWnd) { + return curWnd->getHandle(); + } else { + return ownerWindow; + } +} + +void WindowFrame::onMove(void* data) { + if (hWnd) { + uint32 flags = SWP_NOREPOSITION; + HWND hWndInsertAfter = NULL; + if (visible()) { + if (IsWindowVisible(hWnd)) { + flags |= SWP_NOZORDER; + } else { + flags |= SWP_SHOWWINDOW; + hWndInsertAfter = getParentWindow(); + } + } else { + flags |= SWP_NOZORDER | SWP_NOMOVE | SWP_NOSIZE | SWP_HIDEWINDOW; + } + if (data) { + DeferWindowPos((HDWP)data, hWnd, hWndInsertAfter, left(), top(), width(), height(), flags); + } else { + SetWindowPos(hWnd, hWndInsertAfter, left(), top(), width(), height(), flags); + } + } +} +void WindowFrame::create(std::string const& text, uint32 style, uint32 exStyle) { + Window::create(0, 0, 10, 10, text, style, exStyle, ownerWindow); +} +void WindowFrame::create(std::string const& wndClass, std::string const& text, uint32 style, uint32 exStyle) { + Window::subclass(wndClass, 0, 0, 10, 10, text, style, exStyle, ownerWindow); +} + +LRESULT WindowFrame::onWndMessage(uint32 message, WPARAM wParam, LPARAM lParam) +{ + uint32 result; + if ((result = onMessage(message, wParam, lParam)) != M_UNHANDLED) { + return result; + } + Frame* cur = getParent(); + while (cur && cur->getParent()) cur = cur->getParent(); + RootWindow* frm = dynamic_cast(cur); + if (frm) { + frm->r_message = message; + frm->r_frame = getParent(); + } + result = Window::onWndMessage(message, wParam, lParam); + if (frm) { + frm->r_message = 0; + frm->r_frame = NULL; + } + return result; +} + +/////////////////////////////////////////////////////// + +RootWindow::RootWindow() + : r_message(0) + , r_frame(nullptr) + , c_frame(nullptr) +{ +} +RootWindow::~RootWindow() { +} + +void* RootWindow::beginMoving() { + return BeginDeferWindowPos(32); +} +void RootWindow::endMoving(void* data) { + EndDeferWindowPos((HDWP) data); +} + +LRESULT RootWindow::onControlMessage(HWND hControl, uint32 message, WPARAM wParam, LPARAM lParam) { + if (hControl == NULL) return onMessage(message, wParam, lParam); + Window* control = Window::fromHandle(hControl); + while (control == NULL) { + hControl = GetParent(hControl); + if (hControl == NULL || hControl == hWnd) break; + control = Window::fromHandle(hControl); + } + Frame* cur = dynamic_cast(control); + uint32 result = M_UNHANDLED; + while (cur && (result = cur->onMessage(message, wParam, lParam)) == M_UNHANDLED) { + cur = cur->getParent(); + } + return result; +} + +void RootWindow::setCapture(Frame* frame) { + Frame* cur = frame; + while (cur->getParent()) cur = cur->getParent(); + RootWindow* frm = dynamic_cast(cur); + if (frm) { + if (frm->c_frame) frm->c_frame->onMessage(WM_CAPTURECHANGED, 0, 0); + else SetCapture(frm->getHandle()); + frm->c_frame = frame; + } +} + +LRESULT RootWindow::onWndMessage(uint32 message, WPARAM wParam, LPARAM lParam) { + switch (message) { + case WM_DESTROY: + shutdown(); + break; + case WM_NOTIFY: + { + LRESULT result = onControlMessage(((NMHDR*) lParam)->hwndFrom, message, wParam, lParam); + return result == M_UNHANDLED ? 0 : result; + } + break; + case WM_COMMAND: + { + LRESULT result = onControlMessage((HWND) lParam, message, wParam, lParam); + return result == M_UNHANDLED ? 0 : result; + } + break; + case WM_CTLCOLORBTN: + case WM_CTLCOLORDLG: + case WM_CTLCOLOREDIT: + case WM_CTLCOLORLISTBOX: + case WM_CTLCOLORSCROLLBAR: + case WM_CTLCOLORSTATIC: + { + LRESULT result = onControlMessage((HWND) lParam, message, wParam, lParam); + if (result != M_UNHANDLED) return result; + } + break; + case WM_DRAWITEM: + { + DRAWITEMSTRUCT* dis = (DRAWITEMSTRUCT*) lParam; + if (dis->CtlType != ODT_MENU) { + LRESULT result = onControlMessage(dis->hwndItem, message, wParam, lParam); + if (result != M_UNHANDLED) return result; + } + } + break; + case WM_SIZE: + { + RECT rc; + GetClientRect(hWnd, &rc); + setSize(rc.right, rc.bottom); + } + break; + case WM_CAPTURECHANGED: + case WM_LBUTTONDBLCLK: + case WM_LBUTTONDOWN: + case WM_LBUTTONUP: + case WM_MBUTTONDBLCLK: + case WM_MBUTTONDOWN: + case WM_MBUTTONUP: + case WM_RBUTTONDBLCLK: + case WM_RBUTTONDOWN: + case WM_RBUTTONUP: + case WM_MOUSEWHEEL: + case WM_MOUSEHWHEEL: + case WM_XBUTTONDBLCLK: + case WM_XBUTTONDOWN: + case WM_XBUTTONUP: + case WM_MOUSEMOVE: + if (c_frame) { + if (message != WM_CAPTURECHANGED) { + int x = LOWORD(lParam); + int y = HIWORD(lParam); + x -= c_frame->left(); + y -= c_frame->top(); + lParam = MAKELONG(x, y); + } + uint32 result = c_frame->onMessage(message, wParam, lParam); + if (message == WM_CAPTURECHANGED) c_frame = NULL; + if (result != M_UNHANDLED) return result; + else return 0; + } + break; + } + LRESULT result = M_UNHANDLED; + if (message == r_message && r_frame) { + Frame* cur = r_frame; + while (cur && (result = cur->onMessage(message, wParam, lParam)) == M_UNHANDLED) { + cur = cur->getParent(); + } + } else { + result = onMessage(message, wParam, lParam); + } + if (result != M_UNHANDLED) return result; + return Window::onWndMessage(message, wParam, lParam); +} diff --git a/src/frameui/framewnd.h b/src/frameui/framewnd.h new file mode 100644 index 0000000..b639f22 --- /dev/null +++ b/src/frameui/framewnd.h @@ -0,0 +1,40 @@ +#pragma once + +#include +#include "frameui/frame.h" +#include "frameui/window.h" + +class WindowFrame : public Frame, public Window { + HWND ownerWindow; +protected: + void onMove(void* data); + LRESULT onWndMessage(uint32 message, WPARAM wParam, LPARAM lParam); +protected: + void create(std::string const& text, uint32 style, uint32 exStyle); + void create(std::string const& wndClass, std::string const& text, uint32 style, uint32 exStyle); + HWND getOwner() const { + return ownerWindow; + } + HWND getParentWindow() const; +public: + WindowFrame(Frame* parent); +}; + +class RootWindow : public RootFrame, public Window { + LRESULT onControlMessage(HWND hControl, uint32 message, WPARAM wParam, LPARAM lParam); + LRESULT onWndMessage(uint32 message, WPARAM wParam, LPARAM lParam); + + void* beginMoving(); + void endMoving(void* data); + + friend class WindowFrame; + uint32 r_message; + Frame* r_frame; + + Frame* c_frame; +public: + RootWindow(); + ~RootWindow(); + + static void setCapture(Frame* frame); +}; diff --git a/src/frameui/listctrl.cpp b/src/frameui/listctrl.cpp new file mode 100644 index 0000000..94d0153 --- /dev/null +++ b/src/frameui/listctrl.cpp @@ -0,0 +1,39 @@ +#include "frameui/framewnd.h" +#include "frameui/fontsys.h" +#include "listctrl.h" + +ListFrame::ListFrame(Frame* parent, int id, int style, int styleEx) + : WindowFrame(parent) +{ + create(WC_LISTVIEW, "", WS_CHILD | WS_TABSTOP | style, WS_EX_CLIENTEDGE); + setFont(FontSys::getSysFont()); + setId(id); + ListView_SetExtendedListViewStyle(hWnd, styleEx); +} + +void ListFrame::clear() { + ListView_DeleteAllItems(hWnd); +} +int ListFrame::addItem(std::string const& name, int data) { + LVITEM lvi; + memset(&lvi, 0, sizeof lvi); + lvi.iItem = ListView_GetItemCount(hWnd); + lvi.mask = LVIF_IMAGE | LVIF_TEXT | LVIF_PARAM; + lvi.pszText = const_cast(name.c_str()); + lvi.lParam = data; + ListView_InsertItem(hWnd, &lvi); + return lvi.iItem; +} + +int ListFrame::getCount() const { + return ListView_GetItemCount(hWnd); +} + +int ListFrame::getItemData(int item) { + LVITEM lvi; + memset(&lvi, 0, sizeof lvi); + lvi.iItem = item; + lvi.mask = LVIF_PARAM; + ListView_GetItem(hWnd, &lvi); + return lvi.lParam; +} diff --git a/src/frameui/listctrl.h b/src/frameui/listctrl.h new file mode 100644 index 0000000..c11908c --- /dev/null +++ b/src/frameui/listctrl.h @@ -0,0 +1,24 @@ +#pragma once + +#include "frameui/frame.h" +#include "frameui/window.h" +#include "frameui/framewnd.h" + +#include + +class ListFrame : public WindowFrame { +public: + ListFrame(Frame* parent, int id = 0, + int style = LVS_LIST | LVS_SHOWSELALWAYS, + int styleEx = 0); + + void clear(); + + int getItemData(int item); + int getCount() const; + int addItem(std::string const& name, int data = 0); + + int getCurSel() const { + return ListView_GetNextItem(hWnd, -1, LVNI_SELECTED); + } +}; diff --git a/src/frameui/searchlist.cpp b/src/frameui/searchlist.cpp new file mode 100644 index 0000000..c943386 --- /dev/null +++ b/src/frameui/searchlist.cpp @@ -0,0 +1,474 @@ +#include "searchlist.h" +#include "fontsys.h" +#include +#include + +//////////////////////////////////////// + +LRESULT Scrollable::onMessage(uint32 message, WPARAM wParam, LPARAM lParam) { + SCROLLINFO si; + int step; + switch (message) { + case WM_SIZE: + memset(&si, 0, sizeof si); + si.cbSize = sizeof si; + si.fMask = SIF_PAGE | SIF_DISABLENOSCROLL; + si.nPage = HIWORD(lParam); + SetScrollInfo(hWnd, SB_VERT, &si, TRUE); + scroll(scrollPos); + return 0; + case WM_VSCROLL: + memset(&si, 0, sizeof si); + si.cbSize = sizeof si; + si.fMask = SIF_ALL; + GetScrollInfo(hWnd, SB_VERT, &si); + switch (LOWORD(wParam)) { + case SB_TOP: + si.nPos = si.nMin; + break; + case SB_BOTTOM: + si.nPos = si.nMax; + break; + case SB_LINEUP: + si.nPos -= 16; + break; + case SB_LINEDOWN: + si.nPos += 16; + break; + case SB_PAGEUP: + si.nPos -= si.nPage; + break; + case SB_PAGEDOWN: + si.nPos += si.nPage; + break; + case SB_THUMBTRACK: + si.nPos = si.nTrackPos; + break; + } + scroll(si.nPos); + SetFocus(hWnd); + return 0; + case WM_MOUSEWHEEL: + SystemParametersInfo(SPI_GETWHEELSCROLLLINES, 0, &step, 0); + if (step < 0) step = 3; + scrollAccum += GET_WHEEL_DELTA_WPARAM(wParam) * step * 16; + scroll(scrollPos - scrollAccum / WHEEL_DELTA); + scrollAccum %= WHEEL_DELTA; + return 0; + } + return M_UNHANDLED; +} + +void Scrollable::scroll(int pos) { + SCROLLINFO si; + memset(&si, 0, sizeof si); + si.cbSize = sizeof si; + si.fMask = SIF_RANGE | SIF_PAGE; + GetScrollInfo(hWnd, SB_VERT, &si); + if (pos < si.nMin) pos = si.nMin; + if (pos > si.nMax - si.nPage + 1) pos = si.nMax - si.nPage + 1; + si.fMask = SIF_POS; + if (pos != scrollPos) { + si.nPos = pos; + SetScrollInfo(hWnd, SB_VERT, &si, TRUE); + + int deltaY = scrollPos - pos; + scrollPos = pos; + RECT rc; + GetClientRect(hWnd, &rc); + ScrollWindowEx(hWnd, 0, deltaY, &rc, &rc, NULL, NULL, SW_ERASE | SW_INVALIDATE); + } +} + +//////////////////////////////////////// + +SearchList::SearchList(Frame* parent, int id) + : Scrollable(parent) + , cursel(-1) +{ + itemHeight = FontSys::getTextSize(FontSys::getSysFont(), "123").cy + 3; + + if (WNDCLASSEX* wcx = createclass("SearchListClass")) { + wcx->hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH); + wcx->hCursor = LoadCursor(NULL, IDC_ARROW); + RegisterClassEx(wcx); + } + create("", WS_CHILD | WS_VSCROLL, WS_EX_CLIENTEDGE); + setId(id); +} + +void SearchList::clear() { + items.clear(); + display.clear(); + resize(); +} +void SearchList::insert(uint32 id, std::string const& text) { + items.emplace_back(id, text); +} +void SearchList::insertEx(uint32 id, std::string const& text) { + itemsEx.emplace_back(id, text); +} + +static bool contains(std::vector const& kmp, std::string const& query, std::string const& text) { + if (kmp.empty()) return true; + size_t cur = 0; + for (size_t i = 0; i < text.length(); ++i) { + while (cur && query[cur] != tolower((uint8) text[i])) { + cur = kmp[cur - 1]; + } + cur += (query[cur] == tolower((uint8)text[i])); + if (cur == query.length()) return true; + } + return false; +} + +void SearchList::search(std::string const& query_) { + query = strlower(query_); + std::vector kmp(query.length(), 0); + for (size_t i = 1; i < query.length(); ++i) { + kmp[i] = kmp[i - 1]; + while (kmp[i] && query[kmp[i]] != query[i]) { + kmp[i] = kmp[kmp[i] - 1]; + } + kmp[i] += (query[kmp[i]] == query[i]); + } + Item* sel = nullptr; + if (cursel >= 0) sel = display[cursel]; + display.clear(); + cursel = -1; + scrollPos = 0; + scrollAccum = 0; + auto* list = &items; + if (query.length() && itemsEx.size()) { + list = &itemsEx; + } + for (auto& item : *list) { + if (contains(kmp, query, item.text)) { + if (&item == sel) cursel = display.size(); + display.push_back(&item); + } + } + resize(); +} + +void SearchList::sort() { + std::sort(items.begin(), items.end()); +} +void SearchList::sortEx() { + std::sort(itemsEx.begin(), itemsEx.end()); +} +void SearchList::update() { + search(query); +} +void SearchList::resize() { + RECT rc; + GetClientRect(hWnd, &rc); + SCROLLINFO si; + memset(&si, 0, sizeof si); + si.cbSize = sizeof si; + si.fMask = SIF_RANGE | SIF_PAGE | SIF_DISABLENOSCROLL; + si.nMin = 0; + si.nMax = display.size() * itemHeight; + si.nPage = rc.bottom; + SetScrollInfo(hWnd, SB_VERT, &si, TRUE); + scroll(scrollPos); + + InvalidateRect(hWnd, NULL, TRUE); +} + +void SearchList::render(HDC hDC) { + RECT rc; + GetClientRect(hWnd, &rc); + SelectObject(hDC, FontSys::getSysFont()); + int top = (rc.top + scrollPos) / itemHeight; + int bottom = (rc.bottom + scrollPos + itemHeight) / itemHeight; + if (top < 0) top = 0; + if (bottom > display.size()) bottom = display.size(); + + uint32 fgHighlight = GetSysColor(COLOR_HIGHLIGHTTEXT); + uint32 bgHighlight = GetSysColor(COLOR_HIGHLIGHT); + uint32 fgNormal = 0x000000; + uint32 bgNormal = 0xFFFFFF; + + for (int index = top; index < bottom; ++index) { + Item* item = display[index]; + if (index == cursel) { + SetTextColor(hDC, fgHighlight); + SetBkColor(hDC, bgHighlight); + } else { + SetTextColor(hDC, fgNormal); + SetBkColor(hDC, bgNormal); + } + RECT irc = rc; + irc.top = index * itemHeight - scrollPos; + irc.bottom = irc.top + itemHeight; + ExtTextOut(hDC, 0, 0, ETO_OPAQUE, &irc, NULL, 0, NULL); + DrawText(hDC, item->text.c_str(), item->text.length(), &irc, DT_SINGLELINE | DT_NOPREFIX | DT_LEFT | DT_VCENTER); + } + rc.top = bottom * itemHeight - scrollPos; + if (rc.top < rc.bottom) { + SetBkColor(hDC, bgNormal); + ExtTextOut(hDC, 0, 0, ETO_OPAQUE, &rc, NULL, 0, NULL); + } +} + +LRESULT SearchList::onMessage(uint32 message, WPARAM wParam, LPARAM lParam) { + int x, y, index; + PAINTSTRUCT ps; + HDC hDC; + switch (message) { + case WM_PAINT: + hDC = BeginPaint(hWnd, &ps); + render(hDC); + EndPaint(hWnd, &ps); + return 0; + case WM_LBUTTONDOWN: + SetFocus(hWnd); + x = GET_X_LPARAM(lParam); + y = GET_Y_LPARAM(lParam); + index = (y + scrollPos) / itemHeight; + if (index < 0 || index >= display.size()) { + index = -1; + } + if (index != cursel) { + cursel = index; + notify(WM_COMMAND, MAKELONG(id(), BN_CLICKED), (LPARAM)hWnd); + InvalidateRect(hWnd, NULL, TRUE); + } + return 0; + } + return Scrollable::onMessage(message, wParam, lParam); +} + +/////////////////////////////////////////// + +class OptionList::Editor : public ComboFrame { +public: + Editor(OptionList* parent, int id = 0) + : ComboFrame(parent, id) + { + } + + LRESULT onMessage(uint32 message, WPARAM wParam, LPARAM lParam) override { + if (message == WM_KILLFOCUS || (message == WM_COMMAND && HIWORD(wParam) == CBN_CLOSEUP)) { + hide(); + SetFocus(getOwner()); + } + return M_UNHANDLED; + } +}; + +OptionList::OptionList(Frame* parent, int id) + : Scrollable(parent) + , editor(nullptr) + , cursel(-1) +{ + itemHeight = FontSys::getTextSize(FontSys::getSysFont(), "123").cy + 5; + + if (WNDCLASSEX* wcx = createclass("OptionListClass")) { + wcx->hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH); + wcx->hCursor = LoadCursor(NULL, IDC_ARROW); + RegisterClassEx(wcx); + } + create("", WS_CHILD | WS_VSCROLL | WS_CLIPCHILDREN | WS_CLIPSIBLINGS, WS_EX_CLIENTEDGE); + setId(id); + + editor = new Editor(this); + editor->addString("", -1); +} + +void OptionList::addOption(std::string const& text) { + if (editor) editor->addString(text, options.size()); + options.push_back(text); +} + +void OptionList::update() { + RECT rc; + GetClientRect(hWnd, &rc); + SCROLLINFO si; + memset(&si, 0, sizeof si); + si.cbSize = sizeof si; + si.fMask = SIF_RANGE | SIF_PAGE | SIF_DISABLENOSCROLL; + si.nMin = 0; + si.nMax = items.size() * itemHeight; + si.nPage = rc.bottom; + SetScrollInfo(hWnd, SB_VERT, &si, TRUE); + scroll(scrollPos); + + InvalidateRect(hWnd, NULL, TRUE); +} + +void OptionList::render(HDC hDC) { + RECT rc; + GetClientRect(hWnd, &rc); + SelectObject(hDC, FontSys::getSysFont()); + int top = (rc.top + scrollPos) / itemHeight; + int bottom = (rc.bottom + scrollPos + itemHeight) / itemHeight; + if (top < 0) top = 0; + if (bottom > items.size()) bottom = items.size(); + + uint32 fgNormal = 0x000000; + uint32 bgNormal = 0xFFFFFF; + uint32 bgLine = 0x808080; + + for (int index = top; index < bottom; ++index) { + std::string item = items[index]; + std::string value; + if (choices[index] >= 0) value = options[choices[index]]; + + SetTextColor(hDC, fgNormal); + SetBkColor(hDC, bgNormal); + RECT irc = rc; + irc.top = index * itemHeight - scrollPos; + irc.bottom = irc.top + itemHeight; + if (cursel == index && editor->visible()) { + irc.right = rc.right / 2; + } + ExtTextOut(hDC, 0, 0, ETO_OPAQUE, &irc, NULL, 0, NULL); + irc.top += 1; + irc.bottom -= 1; + irc.left += 1; + irc.right = rc.right / 2 - 2; + DrawText(hDC, item.c_str(), item.length(), &irc, DT_SINGLELINE | DT_NOPREFIX | DT_LEFT | DT_VCENTER); + irc.left = irc.right + 3; + irc.right = rc.right - 1; + if (cursel != index || !editor->visible()) { + DrawText(hDC, value.c_str(), value.length(), &irc, DT_SINGLELINE | DT_NOPREFIX | DT_LEFT | DT_VCENTER); + } + SetBkColor(hDC, bgLine); + irc.right = rc.right / 2; + irc.left = irc.right - 1; + irc.top -= 1; + irc.bottom += 1; + ExtTextOut(hDC, 0, 0, ETO_OPAQUE, &irc, NULL, 0, NULL); + irc.left = rc.left; + irc.right = rc.right; + irc.top = irc.bottom - 1; + ExtTextOut(hDC, 0, 0, ETO_OPAQUE, &irc, NULL, 0, NULL); + } + rc.top = bottom * itemHeight - scrollPos; + if (rc.top < rc.bottom) { + SetBkColor(hDC, bgNormal); + ExtTextOut(hDC, 0, 0, ETO_OPAQUE, &rc, NULL, 0, NULL); + } +} + +LRESULT OptionList::onMessage(uint32 message, WPARAM wParam, LPARAM lParam) { + int x, y, index; + PAINTSTRUCT ps; + HDC hDC; + RECT rc; + switch (message) { + case WM_ERASEBKGND: + return TRUE; + case WM_PAINT: + hDC = BeginPaint(hWnd, &ps); + render(hDC); + EndPaint(hWnd, &ps); + return 0; + case WM_SIZE: + InvalidateRect(hWnd, NULL, TRUE); + break; + case WM_COMMAND: + if (editor && cursel >= 0 && HIWORD(wParam) == CBN_SELCHANGE && (HWND) lParam == editor->getHandle()) { + int sel = editor->getCurSel(); + choices[cursel] = (sel >= 0 ? editor->getItemData(sel) : -1); + notify(WM_OPTIONCHANGE, id(), cursel); + } + return 0; + case WM_LBUTTONDOWN: + SetFocus(hWnd); + x = GET_X_LPARAM(lParam); + y = GET_Y_LPARAM(lParam); + cursel = (y + scrollPos) / itemHeight; + GetClientRect(hWnd, &rc); + if (x < rc.right / 2) cursel = -1; + if (cursel >= 0 && cursel < items.size() && editor) { + editor->setPoint(PT_TOPLEFT, rc.right / 2 - 1, cursel * itemHeight - scrollPos); + editor->setWidth(rc.right / 2 + 3); + editor->show(); + SetFocus(editor->getHandle()); + ComboBox_SetCurSel(editor->getHandle(), choices[cursel] + 1); + ComboBox_ShowDropdown(editor->getHandle(), TRUE); + } else { + cursel = -1; + } + return 0; + } + return Scrollable::onMessage(message, wParam, lParam); +} + +/////////////////////////////////////////// +// +//TestWindow::TestWindow() { +// if (WNDCLASSEX* wcx = createclass("MainWndClass")) { +// wcx->hbrBackground = HBRUSH(COLOR_BTNFACE + 1); +// wcx->hCursor = LoadCursor(NULL, IDC_ARROW); +// RegisterClassEx(wcx); +// } +// create(CW_USEDEFAULT, 0, 400, 700, "Test Window", +// WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN, 0); +// +// query = new EditFrame(this, 100); +// query->setPoint(PT_TOPLEFT, 0, 0); +// query->setPoint(PT_TOPRIGHT, 0, 0); +// query->setHeight(21); +// +// list = new SearchList(this, 101); +// list->setPoint(PT_TOPLEFT, query, PT_BOTTOMLEFT, 0, 4); +// list->setPoint(PT_TOPRIGHT, query, PT_BOTTOMRIGHT, 0, 4); +// list->setHeight(300); +// +// list->insert(0, "fasdfasdf"); +// list->insert(1, "gasdfasdg"); +// list->insert(2, "sadfasdgq"); +// list->insert(3, "gqfeqwe"); +// list->insert(4, "gasdfawe"); +// list->insert(5, "asdfawe"); +// list->insert(6, "gasdfawe"); +// list->insert(7, "asdgawe"); +// list->insert(8, "gasdfawe"); +// list->insert(9, "badsfawe"); +// list->update(); +// +// opts = new OptionList(this, 102); +// opts->setPoint(PT_TOPLEFT, list, PT_BOTTOMLEFT, 0, 4); +// opts->setPoint(PT_BOTTOMRIGHT, 0, 0); +// +// opts->addItem("Item 1", -1); +// opts->addItem("Item 2", 0); +// opts->addItem("Item 3", 1); +// opts->addItem("Item 4", 2); +// opts->addItem("Item 5", 3); +// opts->addOption("Value 1"); +// opts->addOption("Value 2"); +// opts->addOption("Value 3"); +// opts->addOption("Value 4"); +// opts->update(); +//} +// +//LRESULT TestWindow::onMessage(uint32 message, WPARAM wParam, LPARAM lParam) { +// if (message == WM_COMMAND) { +// int id = LOWORD(wParam); +// if (id == 100) { +// if (HIWORD(wParam) == EN_CHANGE) list->search(query->getText()); +// } else if (id == 101) { +// } +// return 0; +// } +// return M_UNHANDLED; +//} +// +//void TestWindow::loop() { +// ShowWindow(hWnd, SW_SHOW); +// while (hWnd) { +// MSG msg; +// if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { +// TranslateMessage(&msg); +// DispatchMessage(&msg); +// if (msg.message == WM_QUIT) { +// DestroyWindow(hWnd); +// } +// } +// } +//} diff --git a/src/frameui/searchlist.h b/src/frameui/searchlist.h new file mode 100644 index 0000000..afbcd35 --- /dev/null +++ b/src/frameui/searchlist.h @@ -0,0 +1,19 @@ +#pragma once + +#include "framewnd.h" +#include "controlframes.h" + +class Scrollable : public WindowFrame { +public: + Scrollable(Frame* parent) + : WindowFrame(parent) + , scrollPos(0) + , scrollAccum(0) + {} + +protected: + int scrollPos; + int scrollAccum; + void scroll(int pos); + LRESULT onMessage(uint32 message, WPARAM wParam, LPARAM lParam) override; +}; diff --git a/src/frameui/window.cpp b/src/frameui/window.cpp new file mode 100644 index 0000000..9afd242 --- /dev/null +++ b/src/frameui/window.cpp @@ -0,0 +1,335 @@ +#include "window.h" + +#include "frameui/fontsys.h" + +#include +#include + +struct Window::TTData { + HWND hTip; + RECT tipRect; + int hitCode; + HFONT hFont; + std::string fixedTip; +}; +static LRESULT CALLBACK TooltipWindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { + if (uMsg == WM_PAINT) { + std::string text = Window::getWindowText(hWnd); + + RECT rc; + GetClientRect(hWnd, &rc); + PAINTSTRUCT ps; + HDC hDC = BeginPaint(hWnd, &ps); + + HFONT hFont = (HFONT)GetWindowLongPtr(hWnd, GWLP_USERDATA); + + SelectObject(hDC, hFont); + SetBkColor(hDC, 0xE1FFFF); + SetTextColor(hDC, 0x000000); + + HPEN hPen = CreatePen(PS_SOLID, 0, 0x000000); + HPEN hPrev = (HPEN)SelectObject(hDC, hPen); + Rectangle(hDC, rc.left, rc.top, rc.right, rc.bottom); + rc.left += 5; + rc.top += 2; + rc.bottom -= 2; + rc.right -= 5; + DrawText(hDC, text.c_str(), text.length(), &rc, DT_LEFT | DT_TOP); + SelectObject(hDC, hPrev); + DeleteObject(hPen); + + EndPaint(hWnd, &ps); + return 0; + } + return DefWindowProc(hWnd, uMsg, wParam, lParam);; +} + +ATOM Window::windowClass = NULL; +std::map Window::handleMap; + +Window* Window::fromHandle(HWND hWnd) { + auto it = handleMap.find(hWnd); + return (it == handleMap.end() ? nullptr : it->second); +} + +WNDCLASSEX* Window::createclass(std::string const& wndClass) { + regClass = wndClass; + + WNDCLASSEX* wcx = new WNDCLASSEX; + HINSTANCE hInstance = GetModuleHandle(NULL); + if (!GetClassInfoEx(hInstance, regClass.c_str(), wcx)) { + memset(wcx, 0, sizeof(WNDCLASSEX)); + wcx->cbSize = sizeof(WNDCLASSEX); + wcx->lpfnWndProc = WindowProc; + wcx->hInstance = hInstance; + wcx->lpszClassName = regClass.c_str(); + wcx->hCursor = LoadCursor(NULL, MAKEINTRESOURCE(IDC_ARROW)); + return wcx; + } + delete wcx; + return NULL; +} +void Window::create(int x, int y, int width, int height, std::string const& text, uint32 style, uint32 exStyle, + HWND parent) +{ + if (!windowClass && regClass.empty()) { + WNDCLASSEX wcex; + memset(&wcex, 0, sizeof wcex); + wcex.cbSize = sizeof wcex; + wcex.lpfnWndProc = WindowProc; + wcex.hInstance = GetModuleHandle(NULL); + wcex.lpszClassName = "WUTILSWINDOW"; + wcex.hCursor = LoadCursor(NULL, MAKEINTRESOURCE(IDC_ARROW)); + windowClass = RegisterClassEx(&wcex); + } + hWnd = CreateWindowEx(exStyle, regClass.empty() ? "WUTILSWINDOW" : regClass.c_str(), text.c_str(), style, + x, y, width, height, parent, NULL, GetModuleHandle(NULL), this); + handleMap[hWnd] = this; +} +void Window::create(std::string const& wndClass, int x, int y, int width, int height, std::string const& text, uint32 style, + uint32 exStyle, HWND parent) { + hWnd = CreateWindowEx(exStyle, wndClass.c_str(), text.c_str(), style, x, y, width, height, + parent, NULL, GetModuleHandle(NULL), NULL); + handleMap[hWnd] = this; +} +void Window::subclass(std::string const& wndClass, int x, int y, int width, int height, std::string const& text, uint32 style, + uint32 exStyle, HWND parent) { + hWnd = CreateWindowEx(exStyle, wndClass.c_str(), text.c_str(), style, x, y, width, height, + parent, NULL, GetModuleHandle(NULL), NULL); + handleMap[hWnd] = this; + origProc = (WNDPROC)SetWindowLongPtr(hWnd, GWLP_WNDPROC, (LONG_PTR) WindowProc); +} + +LRESULT CALLBACK Window::WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { + Window* wnd = fromHandle(hWnd); + if (wnd == NULL && uMsg == WM_CREATE) { + CREATESTRUCT* cs = (CREATESTRUCT*)lParam; + wnd = (Window*)cs->lpCreateParams; + if (wnd) wnd->hWnd = hWnd; + } + if (wnd) { + bool send = true; + if (wnd->ttData) { + TRACKMOUSEEVENT tme; + memset(&tme, 0, sizeof tme); + tme.cbSize = sizeof tme; + tme.hwndTrack = wnd->hWnd; + if (uMsg == WM_MOUSEMOVE) { + POINT pt; + pt.x = GET_X_LPARAM(lParam); + pt.y = GET_Y_LPARAM(lParam); + if (wnd->ttData->hitCode >= 0) { + if (pt.x < wnd->ttData->tipRect.left || pt.x >= wnd->ttData->tipRect.right || + pt.y < wnd->ttData->tipRect.top || pt.y >= wnd->ttData->tipRect.bottom) { + wnd->ttData->hitCode = -1; + } + } + if (wnd->ttData->hitCode < 0) { + ToolInfo ti; + wnd->ttData->hitCode = wnd->toolHitTest(pt, &ti); + if (wnd->ttData->hitCode >= 0) { + tme.dwFlags = TME_HOVER | TME_LEAVE; + tme.dwHoverTime = HOVER_DEFAULT; + TrackMouseEvent(&tme); + + wnd->ttData->tipRect = ti.rc; + SetWindowText(wnd->ttData->hTip, ti.text.c_str()); + + HDC hDC = GetDC(wnd->ttData->hTip); + SelectObject(hDC, wnd->ttData->hFont); + RECT rc = { 0, 0, 1024, 1024 }; + int ht = DrawText(hDC, ti.text.c_str(), ti.text.length(), &rc, DT_LEFT | DT_TOP | DT_CALCRECT); + ReleaseDC(wnd->ttData->hTip, hDC); + + POINT ptTL; + ptTL.x = wnd->ttData->tipRect.left; + ptTL.y = wnd->ttData->tipRect.bottom + 5; + ClientToScreen(wnd->hWnd, &ptTL); + + SetWindowLongPtr(wnd->ttData->hTip, GWLP_USERDATA, (LONG_PTR)wnd->ttData->hFont); + + InvalidateRect(wnd->ttData->hTip, NULL, TRUE); + SetWindowPos(wnd->ttData->hTip, HWND_TOPMOST, + ptTL.x, ptTL.y, rc.right + 10, ht + 5, + SWP_NOACTIVATE); + } else { + tme.dwFlags = TME_CANCEL; + TrackMouseEvent(&tme); + ShowWindow(wnd->ttData->hTip, SW_HIDE); + } + } + } else if (uMsg == WM_MOUSELEAVE) { + ShowWindow(wnd->ttData->hTip, SW_HIDE); + wnd->ttData->hitCode = -1; + send = false; + } else if (uMsg == WM_MOUSEHOVER) { + tme.dwFlags = TME_CANCEL; + TrackMouseEvent(&tme); + + if (wnd->ttData->hitCode >= 0) ShowWindow(wnd->ttData->hTip, SW_SHOWNA); + send = false; + } else if (uMsg == WM_LBUTTONDOWN || uMsg == WM_RBUTTONDOWN || uMsg == WM_MBUTTONDOWN || + uMsg == WM_MOUSEWHEEL || uMsg == WM_MOUSEHWHEEL || uMsg == WM_KEYDOWN) { + ShowWindow(wnd->ttData->hTip, SW_HIDE); + wnd->ttData->hitCode = -1; + } + } + if (send) { + if (uMsg == WM_CLOSE || uMsg == WM_DESTROY) wnd->endModal(); + uint32 result = wnd->onWndMessage(uMsg, wParam, lParam); + if (uMsg == WM_DESTROY) wnd->hWnd = NULL; + return result; + } else { + return 0; + } + } else { + return DefWindowProc(hWnd, uMsg, wParam, lParam); + } +} +LRESULT Window::onWndMessage(uint32 message, WPARAM wParam, LPARAM lParam) { + if (origProc) return CallWindowProc(origProc, hWnd, message, wParam, lParam); + else return DefWindowProc(hWnd, message, wParam, lParam); +} + +Window::Window() { + hWnd = NULL; + origProc = NULL; + ttData = NULL; +} +Window::~Window() { + if (hWnd) { + if (origProc) SetWindowLongPtr(hWnd, GWLP_WNDPROC, (LONG_PTR)origProc); + handleMap.erase(hWnd); + DestroyWindow(hWnd); + } + if (ttData) delete ttData; +} + +void Window::setText(std::string const& text) +{ + SetWindowText(hWnd, text.c_str()); +} +std::string Window::getText() const { + return getWindowText(hWnd); +} +std::string Window::getWindowText(HWND hWnd) { + size_t length = GetWindowTextLength(hWnd); + std::string str; + str.resize(length + 1); + GetWindowText(hWnd, &str[0], str.length()); + str.resize(length); + return str; +} + +void Window::setFont(HFONT hFont) { + SendMessage(hWnd, WM_SETFONT, (WPARAM)hFont, TRUE); +} +HFONT Window::getFont() const { + return (HFONT)SendMessage(hWnd, WM_GETFONT, NULL, NULL); +} + +void Window::enable(bool e) { + EnableWindow(hWnd, e); +} +void Window::showWindow(bool s) { + ShowWindow(hWnd, s ? SW_SHOW : SW_HIDE); +} + +int Window::id() const { + return GetWindowLong(hWnd, GWL_ID); +} +void Window::setId(int id) { + SetWindowLong(hWnd, GWL_ID, id); +} + +void Window::invalidate(bool erase) { + InvalidateRect(hWnd, NULL, erase ? TRUE : FALSE); +} + +void Window::setRedraw(bool r) { + SendMessage(hWnd, WM_SETREDRAW, r ? TRUE : FALSE, 0); + if (r) RedrawWindow(hWnd, NULL, NULL, RDW_INVALIDATE); +} + +int Window::toolHitTest(POINT pt, ToolInfo* ti) { + if (ttData && ttData->fixedTip.length()) { + GetClientRect(hWnd, &ti->rc); + ti->text = ttData->fixedTip; + return 0; + } + return -1; +} +void Window::enableTooltips(bool enable) { + if (hWnd == NULL) return; + if (enable && ttData == NULL) { + ttData = new TTData; + ttData->hFont = FontSys::getSysFont(); + WNDCLASSEX wcx; + HINSTANCE hInstance = GetModuleHandle(NULL); + if (!GetClassInfoEx(hInstance, "DRTooltip", &wcx)) { + memset(&wcx, 0, sizeof wcx); + wcx.cbSize = sizeof wcx; + wcx.lpfnWndProc = TooltipWindowProc; + wcx.hInstance = hInstance; + wcx.lpszClassName = "DRTooltip"; + wcx.hbrBackground = CreateSolidBrush(0xE1FFFF); + RegisterClassEx(&wcx); + } + ttData->hTip = CreateWindowEx(WS_EX_TOPMOST, "DRTooltip", + NULL, WS_POPUP, CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, hWnd, NULL, + hInstance, NULL); + + ttData->hitCode = -1; + } else if (!enable && ttData != NULL) { + DestroyWindow(ttData->hTip); + delete ttData; + ttData = NULL; + } +} +void Window::setTooltipFont(HFONT hFont) { + if (ttData) ttData->hFont = hFont; +} +HFONT Window::getTooltipFont() { + return (ttData ? ttData->hFont : NULL); +} + +void Window::endModal() { + if (continueModal) { + continueModal = false; + SetWindowPos(hWnd, NULL, 0, 0, 0, 0, SWP_HIDEWINDOW | SWP_NOACTIVATE | + SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER); + HWND hParent = GetWindow(hWnd, GW_OWNER); + if (hParent) { + EnableWindow(hParent, TRUE); + if (GetActiveWindow() == hWnd) SetActiveWindow(hParent); + } + PostMessage(hWnd, WM_CLOSE, 0, 0); + } +} +int Window::doModal() { + if (hWnd == NULL) return 0; + ShowWindow(hWnd, SW_SHOW); + HWND hParent = GetWindow(hWnd, GW_OWNER); + if (hParent) EnableWindow(hParent, FALSE); + MSG msg; + continueModal = true; + while (continueModal) { + if (!GetMessage(&msg, NULL, 0, 0)) { + PostQuitMessage(msg.wParam); + return 0; + } + if (!IsDialogMessage(hWnd, &msg)) { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + } + return 0; +} +void Window::setTooltip(char const* tip) { + if (tip && *tip) { + enableTooltips(true); + ttData->fixedTip = tip; + } else { + enableTooltips(false); + } +} diff --git a/src/frameui/window.h b/src/frameui/window.h new file mode 100644 index 0000000..b6f4920 --- /dev/null +++ b/src/frameui/window.h @@ -0,0 +1,77 @@ +#pragma once + +#include +#include +#include "base/types.h" + +class Window +{ + static LRESULT CALLBACK WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam); + static ATOM windowClass; + static std::map handleMap; + struct TTData; + TTData* ttData; + std::string regClass; + bool continueModal; +protected: + HWND hWnd; + WNDPROC origProc; + virtual LRESULT onWndMessage(uint32 message, WPARAM wParam, LPARAM lParam); + WNDCLASSEX* createclass(std::string const& wndClass); + void create(int x, int y, int width, int height, std::string const& text, uint32 style, uint32 exStyle, + HWND parent = NULL); + void create(std::string const& wndClass, int x, int y, int width, int height, std::string const& text, uint32 style, + uint32 exStyle, HWND parent = NULL); + void subclass(std::string const& wndClass, int x, int y, int width, int height, std::string const& text, uint32 style, + uint32 exStyle, HWND parent = NULL); + + struct ToolInfo { + RECT rc; + std::string text; + }; + virtual int toolHitTest(POINT pt, ToolInfo* ti); + void endModal(); +public: + Window(); + virtual ~Window(); + + operator HWND() const { + return hWnd; + } + HWND getHandle() const { + return hWnd; + } + + static Window* fromHandle(HWND hWnd); + + // random functions + void setText(std::string const& text); + std::string getText() const; + + void setFont(HFONT hFont); + HFONT getFont() const; + void setTooltipFont(HFONT hFont); + HFONT getTooltipFont(); + + void enable(bool e = true); + void disable() { + enable(false); + } + void showWindow(bool s = true); + void hideWindow() { + showWindow(false); + } + void setRedraw(bool r); + + int id() const; + void setId(int id); + + void invalidate(bool erase = true); + + void enableTooltips(bool enable = true); + void setTooltip(char const* tip); + + int doModal(); + + static std::string getWindowText(HWND hWnd); +}; diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 0000000..4dd5631 --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,23 @@ +#include "app.h" +#include +#include "base/error.h" + +#if defined _M_IX86 +#pragma comment(linker,"/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='x86' publicKeyToken='6595b64144ccf1df' language='*'\"") +#elif defined _M_IA64 +#pragma comment(linker,"/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='ia64' publicKeyToken='6595b64144ccf1df' language='*'\"") +#elif defined _M_X64 +#pragma comment(linker,"/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='amd64' publicKeyToken='6595b64144ccf1df' language='*'\"") +#else +#pragma comment(linker,"/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"") +#endif + +int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { + try { + Application app(hInstance, hPrevInstance, lpCmdLine, nCmdShow); + return app.run(); + } catch (Exception& ex) { + MessageBox(NULL, ex.what(), "Error", MB_OK | MB_ICONERROR); + return 1; + } +} diff --git a/src/ngdp.cpp b/src/ngdp.cpp new file mode 100644 index 0000000..2ab89e8 --- /dev/null +++ b/src/ngdp.cpp @@ -0,0 +1,457 @@ +#include "ngdp.h" +#include "base/http.h" +#include "base/path.h" +#include "base/checksum.h" +#include + +namespace NGDP { + + const std::string HOST = "http://cn.patch.battle.net:1119"; + + const std::map ProgramCodes = { + {"agent", "Battle.net Agent"}, + {"bna", "Battle.net App"}, + {"bnt", "Heroes of the Storm Alpha (Deprecated)"}, + {"d3", "Diablo 3 Retail"}, + {"d3cn", "Diablo 3 China"}, + {"d3t", "Diablo 3 Test"}, + {"demo", "Demo (Partial)"}, + {"hero", "Heroes of the Storm Retail"}, + {"herot", "Heroes of the Storm Test"}, + {"hsb", "Hearthstone"}, + {"pro", "Overwatch Retail"}, + {"prodev", "Overwatch Dev"}, + {"sc2", "StarCraft II (Partial)"}, + {"s2", "StarCraft II"}, + {"s2t", "StarCraft II Test (Partial)"}, + {"s2b", "StarCraft II Beta"}, + {"test", "Test (Partial)"}, + {"storm", "Heroes of the Storm (Deprecated)"}, + {"war3", "Warcraft III (Partial)"}, + {"wow", "World of Warcraft Retail"}, + {"wowt", "World of Warcraft Test"}, + {"wow_beta", "World of Warcraft Beta"}, + }; + + NGDP::NGDP(std::string const& app) + : program_(app) + { + File file = HttpRequest::get(HOST + "/" + app + "/cdns"); + if (!file) { + throw Exception("failed to fetch cdns file"); + } + for (std::string const& line : file) { + if (line.find('!') != std::string::npos || line.empty()) continue; + auto parts = split(line, '|'); + auto& config = cdns_[parts[0]]; + config.path = parts[1]; + config.hosts = split(parts[2], ' '); + } + + file = HttpRequest::get(HOST + "/" + app + "/versions"); + if (!file) { + throw Exception("failed to fetch versions file"); + } + for (std::string const& line : file) { + if (line.find('!') != std::string::npos || line.empty()) continue; + auto parts = split(line, '|'); + auto& config = versions_[parts[0]]; + config.build = parts[1]; + config.cdn = parts[2]; + config.id = std::stoi(parts[3]); + config.version = parts[4]; + if (cdns_.count(parts[0])) { + regions_.push_back(parts[0]); + } + } + } + + bool NGDP::setRegion(std::string const& region) { + if (!cdns_.count(region) || !versions_.count(region)) { + return false; + } + region_ = region; + base_ = "http://" + cdns_[region].hosts[0] + "/" + cdns_[region].path + "/"; + return true; + } + + std::string NGDP::geturl(std::string const& hash, std::string const& type, bool index) const { + std::string url = base_ + type + "/" + hash.substr(0, 2) + "/" + hash.substr(2, 2) + "/" + hash; + if (index) url += ".index"; + return url; + } + File NGDP::load(std::string const& hash, std::string const& type, bool index) const { + return HttpRequest::get(geturl(hash, type, index)); + } + + File DecodeBLTE(File& blte, uint32 eusize) { + if (blte.read32(true) != 'BLTE') return File(); + uint32 headerSize = blte.read32(true); + if (headerSize) { + std::vector csize; + std::vector usize; + uint16 flags = blte.read16(true); + uint16 chunks = blte.read16(true); + for (uint16 i = 0; i < chunks; ++i) { + csize.push_back(blte.read32(true)); + usize.push_back(blte.read32(true)); + blte.seek(16, SEEK_CUR); + } + MemoryFile dst; + std::vector tmp; + for (uint16 i = 0; i < chunks; ++i) { + uint8 type = blte.read8(); + if (type == 'N') { + if (csize[i] - 1 != usize[i]) return File(); + blte.read(dst.reserve(usize[i]), usize[i]); + } else if (type == 'Z') { + tmp.resize(csize[i] - 1); + blte.read(&tmp[0], tmp.size()); + if (gzinflate(&tmp[0], tmp.size(), dst.reserve(usize[i]), &usize[i])) return File(); + } else { + // unsupported compression + return File(); + } + } + dst.seek(0); + return dst; + } else { + uint64 offset = blte.tell(); + uint64 size = blte.size() - offset; + if (blte.read8() == 'N') { + return blte.subfile(offset, size); + } else if (eusize) { + blte.seek(offset, SEEK_SET); + std::vector tmp(size); + blte.read(&tmp[0], size); + MemoryFile dst; + if (gzinflate(&tmp[0], size, dst.reserve(eusize), &eusize)) return File(); + dst.seek(0); + return dst; + } else { + // unsupported compression + return File(); + } + } + } + + ConfigFile ParseConfig(File& file) { + ConfigFile result; + if (!file) return result; + for (std::string const& line : file) { + if (line[0] == '#') continue; + size_t pos = line.find(" = "); + if (pos == std::string::npos) continue; + result[line.substr(0, pos)] = line.substr(pos + 3); + } + return result; + } + +#pragma pack(push, 1) + struct EncodingFileHeader { + uint16 signature; + uint8 unk; + uint8 sizeA; + uint8 sizeB; + uint16 flagsA; + uint16 flagsB; + uint32 entriesA; + uint32 entriesB; + uint8 unk2; + uint32 stringSize; + }; +#pragma pack(pop) + + void from_string(Hash hash, std::string const& str) { + int val; + for (int i = 0; i < sizeof(Hash); ++i) { + sscanf(&str[i * 2], "%02x", &val); + hash[i] = val; + } + } + std::string to_string(const Hash hash) { + return MD5::format(hash); + } + + Encoding::Encoding(File& file) { + EncodingFileHeader header; + file.read(&header, sizeof header); + flip(header.signature); + flip(header.entriesA); + flip(header.entriesB); + flip(header.stringSize); + if (header.signature != 'EN' || header.sizeA != 16 || header.sizeB != 16) { + throw Exception("invalid encoding file"); + } + + uint32 size = file.size(); + uint32 posHeaderA = sizeof(EncodingFileHeader) + header.stringSize; + uint32 posEntriesA = posHeaderA + header.entriesA * 32; + uint32 posHeaderB = posEntriesA + header.entriesA * 4096; + uint32 posEntriesB = posHeaderB + header.entriesB * 32; + uint32 posLayout = posEntriesB + header.entriesB * 4096; + + data_.resize(header.stringSize + (header.entriesA + header.entriesB) * 4096 + (size - posLayout)); + char* layouts = (char*) &data_[0]; + uint8* entriesA = &data_[header.stringSize]; + uint8* entriesB = entriesA + header.entriesA * 4096; + layout_ = (char*) (entriesB + header.entriesB * 4096); + + file.read(layouts, header.stringSize); + file.seek(posEntriesA, SEEK_SET); + file.read(entriesA, header.entriesA * 4096); + file.seek(posEntriesB, SEEK_SET); + file.read(entriesB, header.entriesB * 4096); + file.read(layout_, size - posLayout); + + for (char* ptr = layouts; ptr < layouts + header.stringSize; ++ptr) { + layouts_.push_back(ptr); + while (*ptr) ++ptr; + } + + file.seek(posHeaderA, SEEK_SET); + encodingTable_.resize(header.entriesA); + for (uint32 i = 0; i < header.entriesA; ++i) { + file.read(encodingTable_[i].hash, sizeof(Hash)); + Hash blockHash, realHash; + file.read(blockHash, sizeof blockHash); + MD5::checksum(entriesA, 4096, realHash); + if (memcmp(realHash, blockHash, sizeof(Hash))) { + throw Exception("encoding file checksum mismatch"); + } + for (uint8* ptr = entriesA; ptr + sizeof(EncodingEntry) <= entriesA + 4096;) { + EncodingEntry* entry = reinterpret_cast(ptr); + if (!entry->keyCount) break; + encodingTable_[i].entries.push_back(entry); + flip(entry->usize); + ptr += sizeof(EncodingEntry) + (entry->keyCount - 1) * sizeof(Hash); + } + entriesA += 4096; + } + + Hash nilHash; + memset(nilHash, 0, sizeof(Hash)); + + file.seek(posHeaderB, SEEK_SET); + layoutTable_.resize(header.entriesB); + for (uint32 i = 0; i < header.entriesB; ++i) { + file.read(layoutTable_[i].key, sizeof(Hash)); + Hash blockHash, realHash; + file.read(blockHash, sizeof blockHash); + MD5::checksum(entriesB, 4096, realHash); + if (memcmp(realHash, blockHash, sizeof(Hash))) { + throw Exception("encoding file checksum mismatch"); + } + for (uint8* ptr = entriesB; ptr + sizeof(LayoutEntry) <= entriesB + 4096;) { + LayoutEntry* entry = reinterpret_cast(ptr); + if (!memcmp(entry->key, nilHash, sizeof(Hash))) { + break; + } + layoutTable_[i].entries.push_back(entry); + flip(entry->stringIndex); + flip(entry->csize); + ptr += sizeof(LayoutEntry); + } + entriesB += 4096; + } + } + + template + typename Vec::const_iterator find_hash(Vec const& vec, Comp less) { + if (vec.empty() || less(vec[0])) return vec.end(); + size_t left = 0, right = vec.size(); + while (right - left > 1) { + size_t mid = (left + right) / 2; + if (less(vec[mid])) { + right = mid; + } else { + left = mid; + } + } + return vec.begin() + left; + } + + Encoding::EncodingEntry const* Encoding::getEncoding(const Hash hash) const { + auto it = find_hash(encodingTable_, [&hash](EncodingHeader const& rhs) { + return memcmp(hash, rhs.hash, sizeof(Hash)) < 0; + }); + if (it == encodingTable_.end()) return nullptr; + auto sub = find_hash(it->entries, [&hash](EncodingEntry const* rhs) { + return memcmp(hash, rhs->hash, sizeof(Hash)) < 0; + }); + if (sub == it->entries.end()) return nullptr; + if (memcmp((*sub)->hash, hash, sizeof(Hash))) return nullptr; + return *sub; + } + Encoding::LayoutEntry const* Encoding::getLayout(const Hash key) const { + auto it = find_hash(layoutTable_, [&key](LayoutHeader const& rhs) { + return memcmp(key, rhs.key, sizeof(Hash)) < 0; + }); + if (it == layoutTable_.end()) return nullptr; + auto sub = find_hash(it->entries, [&key](LayoutEntry const* rhs) { + return memcmp(key, rhs->key, sizeof(Hash)) < 0; + }); + if (sub == it->entries.end()) return nullptr; + if (memcmp((*sub)->key, key, sizeof(Hash))) return nullptr; + return *sub; + } + + CascStorage::CascStorage(std::string const& root) + : root_(root) + { + path::create(root / "config"); + path::create(root / "data"); + path::create(root / "indices"); + path::create(root / "patch"); + + std::vector names; + WIN32_FIND_DATA fdata; + HANDLE hFind = FindFirstFile((root / "data" / "*").c_str(), &fdata); + if (hFind == INVALID_HANDLE_VALUE) return; + do { + if (!(fdata.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) { + names.push_back(fdata.cFileName); + } + } while (FindNextFile(hFind, &fdata)); + FindClose(hFind); + + for (std::string const& name : names) { + DeleteFile((root / "data" / name).c_str()); + } + } + + File& CascStorage::addConfig(std::string const& hash, File& file) { + file.seek(0); + File(root_ / "config" / hash.substr(0, 2) / hash.substr(2, 2) / hash, File::REWRITE).copy(file); + file.seek(0); + return file; + } + File& CascStorage::addIndex(std::string const& hash, File& file) { + file.seek(0); + File(root_ / "indices" / hash + ".index", File::REWRITE).copy(file); + file.seek(0); + return file; + } + File CascStorage::addData(std::string const& name) { + return File(root_ / "data" / name, File::REWRITE); + } + + File& CascStorage::addCache(std::string const& name, File& file) { + file.seek(0); + File(path::root() / "cache" / name, File::REWRITE).copy(file); + file.seek(0); + return file; + } + File CascStorage::addCache(std::string const& name) { + return File(path::root() / "cache" / name, File::MODIFY); + } + File CascStorage::getCache(std::string const& name) { + return File(path::root() / "cache" / name); + } + + DataStorage::DataStorage(CascStorage& storage) + : storage_(storage) + , indexCount_(0) + , dataCount_(0) + { + } + + File& DataStorage::addFile(const Hash hash, File& file) { + if (!file) return file; + if (index_.size() >= MaxIndexEntries) { + writeIndex(); + } + if (!data_ || data_.size() + file.size() + 30 > MaxDataSize) { + data_ = storage_.addData(fmtstring("data.%03u", dataCount_++)); + } + index_.emplace_back(); + auto& entry = index_.back(); + memcpy(entry.hash, hash, sizeof(Hash)); + entry.index = dataCount_ - 1; + entry.offset = data_.tell(); + entry.size = 30 + file.size(); + + for (int i = 15; i >= 0; --i) { + data_.write8(hash[i]); + } + data_.write32(30 + file.size()); + data_.write16(0); + data_.write32(0); + data_.write32(0); + file.seek(0); + data_.copy(file); + file.seek(0); + return file; + } + +#pragma pack(push, 1) + struct IndexHeader { + uint16 version = 7; + uint8 keyIndex; + uint8 extraBytes = 0; + uint8 sizeBytes = 4; + uint8 offsBytes = 5; + uint8 keyBytes = 9; + uint8 segmentBits = 30; + uint64 maxOffset; + }; + struct WriteIndexEntry { + uint8 hash[9]; + uint8 pos[5]; + uint32 size; + }; +#pragma pack(pop) + + void DataStorage::writeIndex() { + if (index_.empty()) return; + + IndexHeader header; + header.keyIndex = indexCount_++; + header.maxOffset = _byteswap_uint64(MaxDataSize); + + File index = storage_.addData(fmtstring("%02x%08x.idx", indexCount_ - 1, 1)); + index.write32(sizeof(IndexHeader)); + index.write32(hashlittle(&header, sizeof header, 0)); + index.write(&header, sizeof header); + + std::sort(index_.begin(), index_.end(), [](IndexEntry const& lhs, IndexEntry const& rhs) { + return memcmp(lhs.hash, rhs.hash, sizeof(Hash)) < 0; + }); + + // pad + index.write32(0); + index.write32(0); + + uint32 blockPos = index.tell(); + uint32 blockSize = 0; + uint32 blockHash = 0; + index.write32(blockSize); + index.write32(blockHash); + for (IndexEntry const& entry : index_) { + WriteIndexEntry write; + memcpy(write.hash, entry.hash, sizeof(write.hash)); + *(uint32*) (write.pos + 1) = _byteswap_ulong(entry.offset); + write.pos[0] = entry.index / 4; + write.pos[1] |= ((entry.index & 3) << 6); + write.size = entry.size; + + index.write(&write, sizeof write); + blockSize += sizeof(write); + blockHash = hashlittle(&write, sizeof write, blockHash); + } + + while (index.tell() & 3) { + index.write8(0); + } + while (index.tell() < 0xA0000) { + index.write32(0); + } + + index.seek(blockPos, SEEK_SET); + index.write32(blockSize); + index.write32(blockHash); + + index_.clear(); + } + +} diff --git a/src/ngdp.h b/src/ngdp.h new file mode 100644 index 0000000..a439bfd --- /dev/null +++ b/src/ngdp.h @@ -0,0 +1,182 @@ +#pragma once +#include "base/common.h" +#include "base/json.h" +#include "base/file.h" +#include + +namespace NGDP { + + extern const std::string HOST; + + extern const std::map ProgramCodes; + + typedef uint8 Hash[16]; + void from_string(Hash hash, std::string const& str); + std::string to_string(const Hash hash); + + struct Hash_container { + Hash _; + struct hash { + size_t operator()(Hash_container const& hash) const { + return *reinterpret_cast(hash._); + } + }; + struct equal { + bool operator()(Hash_container const& lhs, Hash_container const& rhs) const { + return !memcmp(lhs._, rhs._, sizeof(Hash)); + } + }; + static Hash_container const& from(const Hash hash) { + return *reinterpret_cast(hash); + } + }; + + struct CdnData { + std::string path; + std::vector hosts; + }; + struct VersionData { + std::string build; + std::string cdn; + uint32 id; + std::string version; + }; + + class NGDP { + public: + NGDP(std::string const& app); + + std::string const& program() const { + return program_; + } + std::string const& region() const { + return region_; + } + + std::vector regions() const { + return regions_; + } + bool setRegion(std::string const& region); + + VersionData const* version() const { + return getptr(versions_, region_); + } + + CdnData const* cdn() const { + return getptr(cdns_, region_); + } + + std::string geturl(std::string const& hash, std::string const& type = "config", bool index = false) const; + File load(std::string const& hash, std::string const& type = "config", bool index = false) const; + File load(const Hash hash, std::string const& type = "config", bool index = false) const { + return load(to_string(hash), type, index); + } + + private: + std::string program_; + std::string region_; + std::map cdns_; + std::map versions_; + std::vector regions_; + std::string base_; + }; + + File DecodeBLTE(File& blte, uint32 usize = 0); + typedef std::map ConfigFile; + ConfigFile ParseConfig(File& file); + + class Encoding { + public: + Encoding(File& file); + +#pragma pack(push, 1) + struct EncodingEntry { + uint16 keyCount; + uint32 usize; + Hash hash; + Hash keys[1]; + }; + struct LayoutEntry { + Hash key; + uint32 stringIndex; + uint8 unk; + uint32 csize; + }; +#pragma pack(pop) + + EncodingEntry const* getEncoding(const Hash hash) const; + LayoutEntry const* getLayout(const Hash key) const; + + char const* const& layout(uint32 index) const { + return layouts_[index]; + } + char const* const& layout() const { + return layout_; + } + + private: + std::vector data_; + struct EncodingHeader { + Hash hash; + std::vector entries; + }; + struct LayoutHeader { + Hash key; + std::vector entries; + }; + std::vector encodingTable_; + std::vector layoutTable_; + std::vector layouts_; + char* layout_; + }; + + class CascStorage { + public: + CascStorage(std::string const& root); + + File& addConfig(std::string const& hash, File& file); + File& addIndex(std::string const& hash, File& file); + File addData(std::string const& name); + + static File& addCache(std::string const& name, File& file); + static File addCache(std::string const& name); + static File getCache(std::string const& name); + + private: + std::string root_; + }; + + class DataStorage { + public: + DataStorage(CascStorage& storage); + ~DataStorage() { + finish(); + } + + File& addFile(const Hash hash, File& file); // <- original (compressed) file + + void finish() { + writeIndex(); + } + + private: + enum { + MaxIndexEntries = (0x8E000 - 0x28) / 18, + MaxDataSize = 0x40000000, + }; + CascStorage& storage_; + struct IndexEntry { + Hash hash; + uint32 size; + uint16 index; + uint32 offset; + }; + std::vector index_; + File data_; + uint32 indexCount_; + uint32 dataCount_; + + void writeIndex(); + }; + +} diff --git a/src/pages/build.cpp b/src/pages/build.cpp new file mode 100644 index 0000000..4d4fe99 --- /dev/null +++ b/src/pages/build.cpp @@ -0,0 +1,119 @@ +#include "build.h" +#include "ngdp.h" +#include "app.h" +#include "program.h" +#include "tags.h" + +enum { + ID_BUILD = 1000, +}; + +BuildPage::BuildPage(Wizard* wizard) + : Page(wizard) +{ + build_ = new ComboFrame(this, ID_BUILD); + build_->addString("Loading CDN config...", -1); + build_->setCurSel(0); + build_->setPoint(PT_TOPLEFT, 120, 0); + build_->setPoint(PT_RIGHT, 0, 0); + build_->disable(); + ComboBox_SetCueBannerText(build_->getHandle(), L"Select one"); + StaticFrame::addTip(build_, "Select build"); + + tags_ = new EditFrame(this, 0, ES_AUTOHSCROLL | ES_MULTILINE | ES_AUTOVSCROLL | ES_READONLY); + tags_->setPoint(PT_TOP, build_, PT_BOTTOM, 0, 6); + tags_->setPoint(PT_LEFT, 0, 0); + tags_->setPoint(PT_BOTTOMRIGHT, 0, 0); + tags_->hide(); +} + +void BuildPage::init() { + wizard_->enableNext(false); + + auto& data = wizard_->app()->data(); + auto ngdp = data.ngdp(); + if (ngdp && ngdp->version() && ngdp->version()->cdn == data.builds_loaded && data.builds.size() == data.build_configs.size()) { + build_->reset(); + for (std::string const& build : data.builds) { + std::string name = data.build_configs[build]["build-name"]; + if (build == ngdp->version()->build) { + name += " (current)"; + } + build_->addString(name); + if (build == data.selected_build) { + build_->setCurSel(build_->getCount() - 1); + } + } + build_->enable(); + onMessage(WM_COMMAND, MAKELONG(ID_BUILD, CBN_SELCHANGE), 0); + } else { + wizard_->app()->data().loadBuilds(); + } +} + +LRESULT BuildPage::onMessage(uint32 message, WPARAM wParam, LPARAM lParam) { + auto& data = wizard_->app()->data(); + switch (message) { + case WM_COMMAND: + if (LOWORD(wParam) == ID_BUILD && HIWORD(wParam) == CBN_SELCHANGE) { + int index = build_->getCurSel(); + if (index >= 0 && index < data.builds.size() && data.build_configs.count(data.builds[index])) { + static const std::string skipTags = "download|encoding|encoding-size|install|patch|patch-config|root"; + std::string text; + auto const& build = data.build_configs[data.builds[index]]; + data.selected_build = data.builds[index]; + for (auto& kv : build) { + if (skipTags.find(kv.first) != std::string::npos) continue; + text.append(kv.first); + text.append(" = "); + text.append(kv.second); + text.append("\r\n"); + } + tags_->setText(text); + tags_->show(); + wizard_->enableNext(true); + } else { + data.selected_build.clear(); + tags_->hide(); + wizard_->enableNext(false); + } + } + return 0; + case WM_TASKDONE: + if (lParam == -1) { + build_->reset(); + build_->addString("Failed to load CDN config", -1); + build_->setCurSel(0); + build_->disable(); + } else if (lParam == 0) { + build_->reset(); + for (std::string const& build : data.builds) { + build_->addString("Loading...", -1); + } + build_->enable(); + } else if (lParam != ProgramData::LOADING) { + build_->delString(lParam - 1); + std::string build = data.builds[lParam - 1]; + std::string name = data.build_configs[build]["build-name"]; + if (build == data.ngdp()->version()->build) { + name += " (current)"; + } + SendMessage(build_->getHandle(), CB_INSERTSTRING, lParam - 1, (LPARAM) name.c_str()); + } + return 0; + default: + return M_UNHANDLED; + } +} + +Page* BuildPage::getPrev() { + return new ProgramPage(wizard_); +} +Page* BuildPage::getNext() { + auto& data = wizard_->app()->data(); + if (data.build_configs.count(data.selected_build)) { + return new TagsPage(wizard_); + } else { + return nullptr; + } +} diff --git a/src/pages/build.h b/src/pages/build.h new file mode 100644 index 0000000..e1be5bc --- /dev/null +++ b/src/pages/build.h @@ -0,0 +1,19 @@ +#pragma once + +#include "wizard.h" + +class BuildPage : public Page { +public: + BuildPage(Wizard* wizard); + + bool hasPrev() const { return true; } + Page* getPrev(); + bool hasNext() const { return true; } + Page* getNext(); + +private: + ComboFrame* build_; + EditFrame* tags_; + void init(); + LRESULT onMessage(uint32 message, WPARAM wParam, LPARAM lParam); +}; diff --git a/src/pages/download.cpp b/src/pages/download.cpp new file mode 100644 index 0000000..b3d2ae4 --- /dev/null +++ b/src/pages/download.cpp @@ -0,0 +1,178 @@ +#include "download.h" +#include "ngdp.h" +#include "app.h" +#include "tags.h" +#include "frameui/fontsys.h" +#include +#include + +enum { + ID_BROWSE = 1000, + ID_START = 1001, +}; + +DownloadPage::DownloadPage(Wizard* wizard) + : Page(wizard) + , logRead_(0) +{ + StaticFrame* tip = new StaticFrame("Download location:", this); + tip->setPoint(PT_TOPLEFT, 0, 0); + + browse_ = new ButtonFrame("Browse", this, ID_BROWSE); + browse_->setPoint(PT_TOP, tip, PT_BOTTOM, 0, 5); + browse_->setPoint(PT_RIGHT, 0, 0); + browse_->setSize(80, 21); + + path_ = new EditFrame(this); + path_->setPoint(PT_TOPLEFT, tip, PT_BOTTOMLEFT, 0, 5); + path_->setPoint(PT_BOTTOMRIGHT, browse_, PT_BOTTOMLEFT, -8, 0); + path_->setText(path::root()); + + note_ = new StaticFrame("", this); + note_->setPoint(PT_TOPLEFT, path_, PT_BOTTOMLEFT, 0, 8); + + loading_ = new ProgressFrame(this); + loading_->setPoint(PT_BOTTOMLEFT, 0, 0); + loading_->setPoint(PT_BOTTOMRIGHT, 0, 0); + loading_->setHeight(21); + + files_ = new StaticFrame("X", this); + files_->setPoint(PT_BOTTOMLEFT, loading_, PT_TOPLEFT, 0, -6); + files_->setPoint(PT_RIGHT, 0, 0); + + size_ = new StaticFrame("X", this); + size_->setPoint(PT_BOTTOMLEFT, files_, PT_TOPLEFT, 0, 0); + size_->setPoint(PT_RIGHT, 0, 0); + + files_->setText(""); + size_->setText(""); + + log_ = new EditFrame(this, 0, ES_AUTOHSCROLL | ES_MULTILINE | ES_AUTOVSCROLL | ES_READONLY); + log_->setPoint(PT_TOPLEFT, path_, PT_BOTTOMLEFT, 0, 8); + log_->setPoint(PT_BOTTOMRIGHT, size_, PT_TOPRIGHT, 0, -8); + + log_->hide(); + loading_->hide(); + size_->hide(); + files_->hide(); + + start_ = new ButtonFrame("Start", this, ID_START); + start_->setPoint(PT_TOPRIGHT, this, PT_BOTTOMRIGHT, 0, 5); + start_->setSize(100, 21); + + note_->setText("Estimated size: " + formatSize(wizard_->app()->data().downloadSize())); +} + +bool browseForFolder(std::wstring prompt, std::wstring& result) { + IFileDialog* pfd; + if (FAILED(CoCreateInstance(CLSID_FileOpenDialog, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&pfd)))) { + return false; + } + DWORD dwOptions; + if (SUCCEEDED(pfd->GetOptions(&dwOptions))) { + pfd->SetOptions(dwOptions | FOS_PICKFOLDERS); + } + pfd->SetTitle(prompt.c_str()); + if (FAILED(pfd->Show(NULL))) { + pfd->Release(); + return false; + } + IShellItem* psi; + if (FAILED(pfd->GetResult(&psi))) { + pfd->Release(); + return false; + } + wchar_t* str; + if (FAILED(psi->GetDisplayName(SIGDN_DESKTOPABSOLUTEPARSING, &str)) || !str) { + psi->Release(); + pfd->Release(); + return false; + } + result = str; + psi->Release(); + pfd->Release(); + return true; +} + +LRESULT DownloadPage::onMessage(uint32 message, WPARAM wParam, LPARAM lParam) { + auto& data = wizard_->app()->data(); + switch (message) { + case WM_COMMAND: + if (LOWORD(wParam) == ID_BROWSE && HIWORD(wParam) == BN_CLICKED) { + std::wstring path; + if (browseForFolder(L"Select download location", path)) { + SetWindowTextW(path_->getHandle(), path.c_str()); + } + } else if (LOWORD(wParam) == ID_START && HIWORD(wParam) == BN_CLICKED) { + if (data.progress()) { + data.stopDownload(); + start_->setText("Start"); + wizard_->enablePrev(true); + log_->hide(); + loading_->hide(); + size_->hide(); + files_->hide(); + note_->show(); + path_->enable(); + browse_->enable(); + } else { + logRead_ = 0; + logText_ = ""; + data.download(path_->getText()); + start_->setText("Cancel"); + wizard_->enablePrev(false); + log_->show(); + log_->setText(""); + loading_->hide(); + size_->show(); + size_->setText(""); + files_->show(); + files_->setText(""); + note_->hide(); + path_->disable(); + browse_->disable(); + } + } + return 0; + case WM_TASKDONE: + update(lParam == ProgramData::LOADING); + if (lParam == 1) { + loading_->hide(); + start_->setText("Done"); + start_->enable(false); + wizard_->enablePrev(true); + } + return 0; + default: + return M_UNHANDLED; + } +} + +void DownloadPage::update(bool loading) { + auto progress = wizard_->app()->data().progress(); + if (!progress) return; + if (progress->log.size() > logRead_) { + while (progress->log.size() > logRead_) { + if (!logText_.empty()) logText_.append("\r\n"); + logText_.append(progress->log[logRead_++]); + } + log_->setText(logText_); + } + uint64 sizeDone = progress->sizeDone; + if (loading) sizeDone += wizard_->app()->data().loading_progress; + files_->setText(fmtstring("Files: %u/%u", progress->filesDone, progress->filesTotal)); + size_->setText(fmtstring("Downloaded: %s/%s", formatSize(sizeDone).c_str(), formatSize(progress->sizeTotal).c_str())); + size_->show(progress->sizeTotal != 0); + if (progress->sizeTotal) { + loading_->setRange(0, progress->sizeTotal / 1024); + loading_->setPos(sizeDone / 1024); + } else { + loading_->setRange(0, progress->filesTotal); + loading_->setPos(progress->filesDone); + } + loading_->show(); +} + +Page* DownloadPage::getPrev() { + return new TagsPage(wizard_); +} diff --git a/src/pages/download.h b/src/pages/download.h new file mode 100644 index 0000000..e6f984c --- /dev/null +++ b/src/pages/download.h @@ -0,0 +1,28 @@ +#pragma once + +#include "wizard.h" + +class DownloadPage : public Page { +public: + DownloadPage(Wizard* wizard); + + bool hasPrev() const { return true; } + Page* getPrev(); + +private: + EditFrame* path_; + ButtonFrame* start_; + ButtonFrame* browse_; + + StaticFrame* note_; + + EditFrame* log_; + StaticFrame* files_; + StaticFrame* size_; + ProgressFrame* loading_; + size_t logRead_; + std::string logText_; + + LRESULT onMessage(uint32 message, WPARAM wParam, LPARAM lParam); + void update(bool loading); +}; diff --git a/src/pages/program.cpp b/src/pages/program.cpp new file mode 100644 index 0000000..1ca7064 --- /dev/null +++ b/src/pages/program.cpp @@ -0,0 +1,115 @@ +#include "program.h" +#include "ngdp.h" +#include "app.h" +#include "build.h" + +enum { + ID_PROGRAM = 1000, + ID_REGION = 1001, + ID_LINK = 1002, +}; + +ProgramPage::ProgramPage(Wizard* wizard) + : Page(wizard) +{ + StaticFrame* tip = new StaticFrame("Welcome to BlizzGet", this); + tip->setPoint(PT_TOPLEFT, 0, 0); + StaticFrame* tip2 = new StaticFrame("This tool can download any Blizzard game from CDN", this); + tip2->setPoint(PT_TOPLEFT, tip, PT_BOTTOMLEFT, 0, 0); + tip = new StaticFrame("For bugs and suggestions, go to", this); + tip->setPoint(PT_TOPLEFT, tip2, PT_BOTTOMLEFT, 0, 0); + LinkFrame* link = new LinkFrame("https://github.com/d07RiV/blizzget", this, ID_LINK); + link->setPoint(PT_TOPLEFT, tip, PT_BOTTOMLEFT, 0, 0); + + program_ = new ComboFrame(this, ID_PROGRAM); + for (auto const& kv : NGDP::ProgramCodes) { + programs_.push_back(kv.first); + program_->addString(kv.first + " - " + kv.second); + } + program_->setPoint(PT_TOPLEFT, link, PT_BOTTOMLEFT, 120, 16); + program_->setPoint(PT_RIGHT, 0, 0); + ComboBox_SetCueBannerText(program_->getHandle(), L"Select one"); + StaticFrame::addTip(program_, "Program code:"); + + region_ = new ComboFrame(this, ID_REGION); + region_->setPoint(PT_TOPLEFT, program_, PT_BOTTOMLEFT, 0, 8); + region_->setPoint(PT_RIGHT, 0, 0); + region_->addString("Select program first"); + region_->setCurSel(0); + region_->disable(); + ComboBox_SetCueBannerText(region_->getHandle(), L"Select region"); + StaticFrame::addTip(region_, "Region:"); +} +void ProgramPage::init() { + wizard_->enableNext(false); + auto ngdp = wizard_->app()->data().ngdp(); + if (ngdp) { + auto it = std::find(programs_.begin(), programs_.end(), ngdp->program()); + if (it == programs_.end()) return; + program_->setCurSel(it - programs_.begin()); + regions_ = wizard_->app()->data().ngdp()->regions(); + region_->reset(); + for (std::string const& r : regions_) { + region_->addString(r); + } + it = std::find(regions_.begin(), regions_.end(), ngdp->region()); + region_->setCurSel(it == regions_.end() ? -1 : it - regions_.begin()); + region_->enable(); + wizard_->enableNext(ngdp->version()); + } +} + +LRESULT ProgramPage::onMessage(uint32 message, WPARAM wParam, LPARAM lParam) { + switch (message) { + case WM_COMMAND: + if (LOWORD(wParam) == ID_PROGRAM && HIWORD(wParam) == CBN_SELCHANGE) { + int index = program_->getCurSel(); + region_->reset(); + region_->addString("Loading..."); + region_->setCurSel(0); + region_->disable(); + wizard_->app()->data().setProgram(index >= 0 && index < programs_.size() ? programs_[index] : ""); + wizard_->enableNext(false); + } else if (LOWORD(wParam) == ID_REGION && HIWORD(wParam) == CBN_SELCHANGE) { + int index = region_->getCurSel(); + auto ngdp = wizard_->app()->data().ngdp(); + if (ngdp && index >= 0 && index < regions_.size()) { + wizard_->enableNext(ngdp->setRegion(regions_[index])); + } else { + wizard_->enableNext(false); + } + } else if (LOWORD(wParam) == ID_LINK && HIWORD(wParam) == BN_CLICKED) { + ShellExecute(NULL, "open", "https://github.com/d07RiV/blizzget", NULL, NULL, SW_SHOWNORMAL); + } + return 0; + case WM_TASKDONE: + if (lParam == ProgramData::LOADING) return 0; + if (!wizard_->app()->data().ngdp()) { + region_->reset(); + region_->addString("Failed to load region list"); + region_->setCurSel(0); + region_->disable(); + return 0; + } + regions_ = wizard_->app()->data().ngdp()->regions(); + region_->reset(); + for (std::string const& r : regions_) { + region_->addString(r); + } + region_->setCurSel(0); + wizard_->enableNext(wizard_->app()->data().ngdp()->setRegion(regions_[0])); + region_->enable(); + return 0; + default: + return M_UNHANDLED; + } +} + +Page* ProgramPage::getNext() { + auto ngdp = wizard_->app()->data().ngdp(); + if (ngdp && ngdp->version()) { + return new BuildPage(wizard_); + } else { + return nullptr; + } +} diff --git a/src/pages/program.h b/src/pages/program.h new file mode 100644 index 0000000..3f08b7c --- /dev/null +++ b/src/pages/program.h @@ -0,0 +1,20 @@ +#pragma once + +#include "wizard.h" + +class ProgramPage : public Page { +public: + ProgramPage(Wizard* wizard); + + bool hasNext() const { return true; } + Page* getNext(); + +private: + ComboFrame* program_; + ComboFrame* region_; + void init(); + LRESULT onMessage(uint32 message, WPARAM wParam, LPARAM lParam); + + std::vector programs_; + std::vector regions_; +}; diff --git a/src/pages/tags.cpp b/src/pages/tags.cpp new file mode 100644 index 0000000..0ec89bf --- /dev/null +++ b/src/pages/tags.cpp @@ -0,0 +1,153 @@ +#include "tags.h" +#include "ngdp.h" +#include "app.h" +#include "build.h" +#include "download.h" +#include "frameui/fontsys.h" +#include + +enum { + ID_ADD = 1000, + ID_REMOVE = 1001, +}; + +TagsPage::TagsPage(Wizard* wizard) + : Page(wizard) + , tags_(nullptr) + , remove_(nullptr) +{ + note_ = new StaticFrame("Fetching encoding table...", this); + note_->setPoint(PT_TOPLEFT, 32, 0); + loading_ = new ProgressFrame(this); + loading_->setPoint(PT_TOPLEFT, note_, PT_BOTTOMLEFT, 0, 8); + loading_->setPoint(PT_RIGHT, -32, 0); + loading_->setHeight(21); + loading_->hide(); +} + +void TagsPage::init() { + wizard_->enableNext(false); + wizard_->app()->data().loadTags(); +} + +void TagsPage::addTags(int index, std::string str) { + auto& data = wizard_->app()->data(); + if (index >= options_.size()) { + tags_->addItem(str); + data.used_tags.push_back(str); + } else { + if (index) str.push_back(' '); + int sel = options_[index]->getCurSel(); + if (sel >= 0) sel = options_[index]->getItemData(sel); + if (sel < 0) { + for (auto const& tag : data.tags[index]) { + addTags(index + 1, str + tag); + } + } else { + addTags(index + 1, str + data.tags[index][sel]); + } + } +} + +LRESULT TagsPage::onMessage(uint32 message, WPARAM wParam, LPARAM lParam) { + auto& data = wizard_->app()->data(); + switch (message) { + case WM_NOTIFY: + if (((NMHDR*) lParam)->code == LVN_ITEMCHANGED) { + int sel = tags_->getCurSel(); + remove_->enable(sel >= 0); + } + return 0; + case WM_COMMAND: + if (LOWORD(wParam) == ID_ADD && HIWORD(wParam) == BN_CLICKED) { + addTags(0, ""); + size_->setText("Total size: " + formatSize(data.downloadSize())); + } else if (LOWORD(wParam) == ID_REMOVE && HIWORD(wParam) == BN_CLICKED) { + int pos; + while ((pos = tags_->getCurSel()) >= 0) { + ListView_DeleteItem(tags_->getHandle(), pos); + data.used_tags.erase(data.used_tags.begin() + pos); + } + size_->setText("Total size: " + formatSize(data.downloadSize())); + } + return 0; + case WM_TASKDONE: + if (lParam == -1) { + loading_->hide(); + note_->setText("Failed to load build tags"); + } else if (lParam == ProgramData::LOADING + 0 || lParam == ProgramData::LOADING + 1) { + if (lParam == ProgramData::LOADING + 0) { + note_->setText("Fetching encoding table..."); + } else { + note_->setText("Fetching download list..."); + } + loading_->setRange(0, data.loading_size); + loading_->setPos(data.loading_progress); + loading_->show(data.loading_progress < data.loading_size); + } else { + loading_->hide(); + note_->setText("Select build tags:"); + int curWidth = 1000; + Frame* prevBox = note_; + Frame* prevLine = note_; + for (auto const& tags : data.tags) { + int maxWidth = std::max(60, FontSys::getTextSize(FontSys::getSysFont(), "Any").cx); + ComboFrame* box = new ComboFrame(this); + options_.push_back(box); + if (tags.size() > 1) { + box->addString("Any", -1); + } + for (size_t i = 0; i < tags.size(); ++i) { + box->addString(tags[i], i); + maxWidth = std::max(maxWidth, FontSys::getTextSize(FontSys::getSysFont(), tags[i]).cx); + } + box->setCurSel(0); + box->setWidth(maxWidth + 20); + if (curWidth + maxWidth + 28 > 300) { + box->setPoint(PT_TOPLEFT, prevLine, PT_BOTTOMLEFT, 0, 8); + prevLine = box; + curWidth = maxWidth + 20; + } else { + box->setPoint(PT_BOTTOMLEFT, prevBox, PT_BOTTOMRIGHT, 8, 0); + curWidth += maxWidth + 28; + } + prevBox = box; + } + + ButtonFrame* btn = new ButtonFrame("+", this, ID_ADD); + btn->setSize(21, 21); + btn->setPoint(PT_TOPRIGHT, note_, PT_BOTTOMLEFT, -8, 8); + + size_ = new StaticFrame("Total size", this); + size_->setPoint(PT_LEFT, note_, PT_LEFT, 0, 0); + size_->setPoint(PT_BOTTOMRIGHT, 0, 0); + + tags_ = new ListFrame(this); + tags_->setPoint(PT_TOPLEFT, prevLine, PT_BOTTOMLEFT, 0, 8); + tags_->setPoint(PT_BOTTOMRIGHT, size_, PT_TOPRIGHT, 0, -8); + + remove_ = new ButtonFrame("-", this, ID_REMOVE); + remove_->setSize(21, 21); + remove_->setPoint(PT_TOPRIGHT, tags_, PT_TOPLEFT, -8, 0); + remove_->disable(); + + if (data.used_tags.size()) { + for (std::string const& tag : data.used_tags) { + tags_->addItem(tag); + } + } + size_->setText("Total size: " + formatSize(data.downloadSize())); + wizard_->enableNext(true); + } + return 0; + default: + return M_UNHANDLED; + } +} + +Page* TagsPage::getPrev() { + return new BuildPage(wizard_); +} +Page* TagsPage::getNext() { + return new DownloadPage(wizard_); +} diff --git a/src/pages/tags.h b/src/pages/tags.h new file mode 100644 index 0000000..f6d6d24 --- /dev/null +++ b/src/pages/tags.h @@ -0,0 +1,25 @@ +#pragma once + +#include "wizard.h" +#include "frameui/listctrl.h" + +class TagsPage : public Page { +public: + TagsPage(Wizard* wizard); + + bool hasPrev() const { return true; } + Page* getPrev(); + bool hasNext() const { return true; } + Page* getNext(); + +private: + StaticFrame* note_; + StaticFrame* size_; + ProgressFrame* loading_; + void addTags(int index, std::string str); + ButtonFrame* remove_; + std::vector options_; + ListFrame* tags_; + void init(); + LRESULT onMessage(uint32 message, WPARAM wParam, LPARAM lParam); +}; diff --git a/src/wizard.cpp b/src/wizard.cpp new file mode 100644 index 0000000..ac71631 --- /dev/null +++ b/src/wizard.cpp @@ -0,0 +1,80 @@ +#include "wizard.h" +#include "app.h" + +#include "resource.h" + +enum { + ID_BUTTON_PREV = 7000, + ID_BUTTON_NEXT = 7001, +}; + +Wizard::Wizard(Application* app) + : app_(app) + , page_(nullptr) + , buttonPrev_(nullptr) + , buttonNext_(nullptr) + , hIcon(LoadIcon(GetModuleHandle(NULL), MAKEINTRESOURCE(IDI_BATTLE_130))) +{ + if (WNDCLASSEX* wcx = createclass("MainWndClass")) { + wcx->hbrBackground = HBRUSH(COLOR_BTNFACE + 1); + wcx->hCursor = LoadCursor(NULL, IDC_ARROW); + wcx->hIcon = hIcon; + RegisterClassEx(wcx); + } + create(CW_USEDEFAULT, 0, 400, 500, "Blizzard Downloader", + (WS_OVERLAPPEDWINDOW & (~WS_MAXIMIZEBOX)) | WS_CLIPCHILDREN, WS_EX_CONTROLPARENT); + + buttonPrev_ = new ButtonFrame("Back", this, ID_BUTTON_PREV); + buttonPrev_->setSize(100, 21); + buttonPrev_->setPoint(PT_BOTTOMLEFT, 10, -10); + buttonPrev_->hide(); + buttonNext_ = new ButtonFrame("Next", this, ID_BUTTON_NEXT); + buttonNext_->setSize(100, 21); + buttonNext_->setPoint(PT_BOTTOMRIGHT, -10, -10); + buttonNext_->hide(); + + showWindow(); +} + +void Wizard::setPage(Page* page) { + app_->data().stop(); + delete page_; + page_ = page; + page->setPoint(PT_TOPLEFT, 10, 10); + page->setPoint(PT_BOTTOMRIGHT, buttonNext_, PT_TOPRIGHT, 0, -5); + buttonPrev_->enable(true); + buttonNext_->enable(true); + buttonPrev_->show(page->hasPrev()); + buttonNext_->show(page->hasNext()); + page->show(); + page->init(); +} + +LRESULT Wizard::onMessage(uint32 message, WPARAM wParam, LPARAM lParam) { + switch (message) { + case WM_COMMAND: + if (LOWORD(wParam) == ID_BUTTON_PREV && HIWORD(wParam) == BN_CLICKED) { + if (page_ && page_->hasPrev()) { + Page* page = page_->getPrev(); + if (page) setPage(page); + } + } else if (LOWORD(wParam) == ID_BUTTON_NEXT && HIWORD(wParam) == BN_CLICKED) { + if (page_ && page_->hasNext()) { + Page* page = page_->getNext(); + if (page) setPage(page); + } + } + return 0; + case WM_DESTROY: + PostQuitMessage(0); + break; + case WM_TASKDONE: + if (page_ == (Page*) wParam) { + page_->onMessage(WM_TASKDONE, 0, lParam); + } + return 0; + default: + return M_UNHANDLED; + } + return 0; +} diff --git a/src/wizard.h b/src/wizard.h new file mode 100644 index 0000000..aaf9b9b --- /dev/null +++ b/src/wizard.h @@ -0,0 +1,55 @@ +#pragma once + +#include "frameui/framewnd.h" +#include "frameui/controlframes.h" + +class Application; +class Page; + +class Wizard : public RootWindow { +public: + Wizard(Application* app); + + Page* page() { + return page_; + } + void setPage(Page* page); + + void enablePrev(bool enable) { + buttonPrev_->enable(enable); + } + void enableNext(bool enable) { + buttonNext_->enable(enable); + } + + Application* app() const { + return app_; + } + +private: + HICON hIcon; + Application* app_; + Page* page_; + ButtonFrame* buttonPrev_; + ButtonFrame* buttonNext_; + LRESULT onMessage(uint32 message, WPARAM wParam, LPARAM lParam); +}; + +class Page : public Frame { +public: + Page(Wizard* wizard) + : Frame(wizard) + , wizard_(wizard) + { + hide(); + } + + virtual void init() {} + virtual bool hasNext() const { return false; } + virtual bool hasPrev() const { return false; } + virtual Page* getNext() { return nullptr; } + virtual Page* getPrev() { return nullptr; } + +protected: + Wizard* wizard_; +}; diff --git a/vc13/Battle_130.ico b/vc13/Battle_130.ico new file mode 100644 index 0000000..c537310 Binary files /dev/null and b/vc13/Battle_130.ico differ diff --git a/vc13/BlizzGet.rc b/vc13/BlizzGet.rc new file mode 100644 index 0000000..dc83d16 Binary files /dev/null and b/vc13/BlizzGet.rc differ diff --git a/vc13/BlizzGet.sln b/vc13/BlizzGet.sln new file mode 100644 index 0000000..bd3df02 --- /dev/null +++ b/vc13/BlizzGet.sln @@ -0,0 +1,22 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 2013 +VisualStudioVersion = 12.0.30723.0 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "BlizzGet", "BlizzGet.vcxproj", "{5C7B85D1-E985-4331-862E-B4864324DCB5}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {5C7B85D1-E985-4331-862E-B4864324DCB5}.Debug|Win32.ActiveCfg = Debug|Win32 + {5C7B85D1-E985-4331-862E-B4864324DCB5}.Debug|Win32.Build.0 = Debug|Win32 + {5C7B85D1-E985-4331-862E-B4864324DCB5}.Release|Win32.ActiveCfg = Release|Win32 + {5C7B85D1-E985-4331-862E-B4864324DCB5}.Release|Win32.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/vc13/BlizzGet.vcxproj b/vc13/BlizzGet.vcxproj new file mode 100644 index 0000000..b6e742b --- /dev/null +++ b/vc13/BlizzGet.vcxproj @@ -0,0 +1,155 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + + {5C7B85D1-E985-4331-862E-B4864324DCB5} + Win32Proj + BlizzGet + + + + Application + true + v120 + MultiByte + + + Application + false + v120 + true + MultiByte + + + + + + + + + + + + + true + + + false + + + + + + Level2 + Disabled + WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions) + ../libs;../src;./ + + + Windows + true + ../libs + comctl32.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + + + Level2 + + + MaxSpeed + true + true + WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions) + ./;../libs/;../src + MultiThreaded + + + Windows + true + true + true + ../libs + comctl32.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + libcmt.lib + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/vc13/BlizzGet.vcxproj.filters b/vc13/BlizzGet.vcxproj.filters new file mode 100644 index 0000000..cb13f08 --- /dev/null +++ b/vc13/BlizzGet.vcxproj.filters @@ -0,0 +1,226 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + {49b335fb-4d74-4e3a-bd5a-30213f6bc659} + + + {77292007-2477-4c3b-8cf3-188ebcd657fb} + + + {21d34fab-6962-4f59-9819-d2078d91e4a7} + + + {a3f7604a-19e5-4519-98cd-d883b7400bac} + + + {7fbda502-e098-492a-a874-85590a929e03} + + + {ca7a40cf-b104-4d1b-bee4-a3b11be5f813} + + + {5bf51ea9-d4c9-43e0-aed6-ef53f22e288d} + + + {7f32c3d0-bdb8-48e1-b2ff-a6f827929e83} + + + {4f824606-2631-4807-a689-de2b0ebe5350} + + + + + Base\Source Files + + + Base\Source Files + + + Base\Source Files + + + Base\Source Files + + + Base\Source Files + + + Base\Source Files + + + Base\Source Files + + + Base\Source Files + + + Base\Source Files + + + Base\Source Files + + + Base\Source Files + + + Base\Source Files + + + FrameUI\Source Files + + + FrameUI\Source Files + + + FrameUI\Source Files + + + FrameUI\Source Files + + + FrameUI\Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Pages\Source Files + + + Source Files + + + Pages\Source Files + + + FrameUI\Source Files + + + Pages\Source Files + + + Pages\Source Files + + + + + Base\Header Files + + + Base\Header Files + + + Base\Header Files + + + Base\Header Files + + + Base\Header Files + + + Base\Header Files + + + Base\Header Files + + + Base\Header Files + + + Base\Header Files + + + Base\Header Files + + + Base\Header Files + + + Base\Header Files + + + Base\Header Files + + + Base\Header Files + + + FrameUI\Header Files + + + FrameUI\Header Files + + + FrameUI\Header Files + + + FrameUI\Header Files + + + FrameUI\Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Pages\Header Files + + + Header Files + + + Pages\Header Files + + + Pages\Header Files + + + FrameUI\Header Files + + + Pages\Header Files + + + + + Resource Files + + + + + Resource Files + + + \ No newline at end of file diff --git a/vc13/resource.h b/vc13/resource.h new file mode 100644 index 0000000..e925364 Binary files /dev/null and b/vc13/resource.h differ