mirror of
https://github.com/CDAGaming/blizzget
synced 2026-02-01 15:12:45 +03:00
initial release
This commit is contained in:
parent
ca74d0450e
commit
348199501d
BIN
libs/zlib/vc100.pdb
Normal file
BIN
libs/zlib/vc100.pdb
Normal file
Binary file not shown.
511
libs/zlib/zconf.h
Normal file
511
libs/zlib/zconf.h
Normal file
@ -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 <windows.h>
|
||||||
|
/* 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 <limits.h>
|
||||||
|
# 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 <sys/types.h> /* for off_t */
|
||||||
|
# endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(STDC) || defined(Z_HAVE_STDARG_H)
|
||||||
|
# ifndef Z_SOLO
|
||||||
|
# include <stdarg.h> /* for va_list */
|
||||||
|
# endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
# ifndef Z_SOLO
|
||||||
|
# include <stddef.h> /* 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 <unistd.h> /* for SEEK_*, off_t, and _LFS64_LARGEFILE */
|
||||||
|
# ifdef VMS
|
||||||
|
# include <unixio.h> /* 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 */
|
||||||
1768
libs/zlib/zlib.h
Normal file
1768
libs/zlib/zlib.h
Normal file
File diff suppressed because it is too large
Load Diff
BIN
libs/zlib/zlib32d.lib
Normal file
BIN
libs/zlib/zlib32d.lib
Normal file
Binary file not shown.
BIN
libs/zlib/zlib32r.lib
Normal file
BIN
libs/zlib/zlib32r.lib
Normal file
Binary file not shown.
BIN
libs/zlib/zlib64d.lib
Normal file
BIN
libs/zlib/zlib64d.lib
Normal file
Binary file not shown.
BIN
libs/zlib/zlib64r.lib
Normal file
BIN
libs/zlib/zlib64r.lib
Normal file
Binary file not shown.
253
libs/zlib/zutil.h
Normal file
253
libs/zlib/zutil.h
Normal file
@ -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 <stddef.h>
|
||||||
|
# endif
|
||||||
|
# include <string.h>
|
||||||
|
# include <stdlib.h>
|
||||||
|
#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 <alloc.h>
|
||||||
|
# endif
|
||||||
|
# else /* MSC or DJGPP */
|
||||||
|
# include <malloc.h>
|
||||||
|
# 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 <malloc.h>
|
||||||
|
# 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 <unix.h> /* 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 <stdio.h>
|
||||||
|
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 */
|
||||||
36
src/app.cpp
Normal file
36
src/app.cpp
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
#include "app.h"
|
||||||
|
#include <commctrl.h>
|
||||||
|
#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;
|
||||||
|
}
|
||||||
28
src/app.h
Normal file
28
src/app.h
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <windows.h>
|
||||||
|
#include <memory>
|
||||||
|
#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_;
|
||||||
|
};
|
||||||
223
src/base/checksum.cpp
Normal file
223
src/base/checksum.cpp
Normal file
@ -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;
|
||||||
|
}
|
||||||
33
src/base/checksum.h
Normal file
33
src/base/checksum.h
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "base/types.h"
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
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);
|
||||||
127
src/base/common.cpp
Normal file
127
src/base/common.cpp
Normal file
@ -0,0 +1,127 @@
|
|||||||
|
#include <algorithm>
|
||||||
|
#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<Bytef*>(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<Bytef*>(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<Bytef*>(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<Bytef*>(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<double>(usize);
|
||||||
|
if (size < 100 * 1024) {
|
||||||
|
return fmtstring("%u B", static_cast<uint32>(size));
|
||||||
|
} else if (size < 10.0 * 1024 * 1024) {
|
||||||
|
return fmtstring("%u K", static_cast<uint32>(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);
|
||||||
|
}
|
||||||
|
}
|
||||||
78
src/base/common.h
Normal file
78
src/base/common.h
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "types.h"
|
||||||
|
#include <string>
|
||||||
|
#include <sstream>
|
||||||
|
#include <cctype>
|
||||||
|
#include <vector>
|
||||||
|
#include <map>
|
||||||
|
#define NOMINMAX
|
||||||
|
#include <windows.h>
|
||||||
|
|
||||||
|
#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<class To>
|
||||||
|
using Map = std::map<istring, To>;
|
||||||
|
typedef Map<std::string> Dictionary;
|
||||||
|
|
||||||
|
template<class MapType, class Key>
|
||||||
|
typename MapType::mapped_type* getptr(MapType& map, Key const& key) {
|
||||||
|
auto it = map.find(key);
|
||||||
|
return (it == map.end() ? nullptr : &it->second);
|
||||||
|
}
|
||||||
|
template<class MapType, class Key>
|
||||||
|
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<int TS>
|
||||||
|
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<typename T>
|
||||||
|
void flip(T& x) {
|
||||||
|
typedef FlipTraits<sizeof(T)> Flip;
|
||||||
|
x = static_cast<T>(Flip::flip(static_cast<Flip::T>(x)));
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string formatSize(uint64 size);
|
||||||
16
src/base/error.cpp
Normal file
16
src/base/error.cpp
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
#include "error.h"
|
||||||
|
#include "types.h"
|
||||||
|
#include <stdarg.h>
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
31
src/base/error.h
Normal file
31
src/base/error.h
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
|
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<class T>
|
||||||
|
inline void append(T const& t) {
|
||||||
|
buf_ << t;
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
mutable std::string str_;
|
||||||
|
std::stringstream buf_;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
static inline Exception&& operator<<(Exception&& e, T const& t) {
|
||||||
|
e.append(t);
|
||||||
|
return std::forward<Exception>(e);
|
||||||
|
}
|
||||||
399
src/base/file.cpp
Normal file
399
src/base/file.cpp
Normal file
@ -0,0 +1,399 @@
|
|||||||
|
#include "file.h"
|
||||||
|
#include <windows.h>
|
||||||
|
#include <set>
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
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<MemoryBuffer*>(file_);
|
||||||
|
return (buffer ? buffer->data() : nullptr);
|
||||||
|
}
|
||||||
|
uint8* MemoryFile::reserve(uint32 size) {
|
||||||
|
MemoryBuffer* buffer = dynamic_cast<MemoryBuffer*>(file_);
|
||||||
|
return (buffer ? buffer->reserve(size) : nullptr);
|
||||||
|
}
|
||||||
|
void MemoryFile::resize(uint32 size) {
|
||||||
|
MemoryBuffer* buffer = dynamic_cast<MemoryBuffer*>(file_);
|
||||||
|
if (buffer) buffer->resize(size);
|
||||||
|
}
|
||||||
|
size_t MemoryFile::csize() const {
|
||||||
|
MemoryBuffer* buffer = dynamic_cast<MemoryBuffer*>(file_);
|
||||||
|
return (buffer ? buffer->size() : 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void File::copy(File& src, uint64 size) {
|
||||||
|
auto mem = dynamic_cast<MemoryBuffer*>(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<size_t>(sizeof buf, size))) {
|
||||||
|
write(buf, count);
|
||||||
|
size -= count;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#include "checksum.h"
|
||||||
|
void File::md5(void* digest) {
|
||||||
|
auto mem = dynamic_cast<MemoryBuffer*>(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;
|
||||||
|
}
|
||||||
251
src/base/file.h
Normal file
251
src/base/file.h
Normal file
@ -0,0 +1,251 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include "common.h"
|
||||||
|
#include "path.h"
|
||||||
|
#include <string>
|
||||||
|
#include <intrin.h>
|
||||||
|
|
||||||
|
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<class T>
|
||||||
|
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<class T>
|
||||||
|
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<std::string> files;
|
||||||
|
std::vector<std::string> folders;
|
||||||
|
};
|
||||||
|
|
||||||
|
File load(char const* name) {
|
||||||
|
return File(root / name);
|
||||||
|
}
|
||||||
|
SearchResults search(char const* mask);
|
||||||
|
};
|
||||||
77
src/base/functor.h
Normal file
77
src/base/functor.h
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
template<class Ret, class... Args>
|
||||||
|
class Functor {
|
||||||
|
class FunctionBase {
|
||||||
|
public:
|
||||||
|
virtual ~FunctionBase() {}
|
||||||
|
virtual Ret run(Args... args) const = 0;
|
||||||
|
};
|
||||||
|
template<class F>
|
||||||
|
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<class F>
|
||||||
|
Functor(F const& f)
|
||||||
|
: func_(new Function<F>(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... Args>
|
||||||
|
class FunctorNoRet {
|
||||||
|
class FunctionBase {
|
||||||
|
public:
|
||||||
|
virtual ~FunctionBase() {}
|
||||||
|
virtual void run(Args... args) const = 0;
|
||||||
|
};
|
||||||
|
template<class F>
|
||||||
|
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<class F>
|
||||||
|
FunctorNoRet(F const& f)
|
||||||
|
: func_(new Function<F>(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...);
|
||||||
|
}
|
||||||
|
};
|
||||||
216
src/base/http.cpp
Normal file
216
src/base/http.cpp
Normal file
@ -0,0 +1,216 @@
|
|||||||
|
#define NOMINMAX
|
||||||
|
#include "http.h"
|
||||||
|
#include "common.h"
|
||||||
|
#include "types.h"
|
||||||
|
#include <algorithm>
|
||||||
|
#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<HttpRequest::SessionHolder> handles_;
|
||||||
|
size_t size_;
|
||||||
|
uint8* data_;
|
||||||
|
size_t pos_;
|
||||||
|
size_t loaded_;
|
||||||
|
public:
|
||||||
|
HttpBuffer(std::shared_ptr<HttpRequest::SessionHolder> 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<DWORD>(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<std::string, std::string> HttpRequest::headers() {
|
||||||
|
std::map<std::string, std::string> 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<wchar_t> 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;
|
||||||
|
}
|
||||||
38
src/base/http.h
Normal file
38
src/base/http.h
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <windows.h>
|
||||||
|
#include <winhttp.h>
|
||||||
|
#include <memory>
|
||||||
|
#include <string>
|
||||||
|
#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<std::string, std::string> 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<SessionHolder> handles_;
|
||||||
|
RequestType type_;
|
||||||
|
std::wstring headers_;
|
||||||
|
std::string post_;
|
||||||
|
};
|
||||||
989
src/base/json.cpp
Normal file
989
src/base/json.cpp
Normal file
@ -0,0 +1,989 @@
|
|||||||
|
#include "json.h"
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
namespace json {
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
struct Defaults {
|
||||||
|
static T value_;
|
||||||
|
};
|
||||||
|
template<class T>
|
||||||
|
T Defaults<T>::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<double>(val);
|
||||||
|
} else {
|
||||||
|
setType(tInteger);
|
||||||
|
int_ = static_cast<int>(val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Value::Value(sint32 val)
|
||||||
|
: type_(tUndefined)
|
||||||
|
{
|
||||||
|
setType(tInteger);
|
||||||
|
int_ = static_cast<int>(val);
|
||||||
|
}
|
||||||
|
Value::Value(uint32 val)
|
||||||
|
: type_(tUndefined)
|
||||||
|
{
|
||||||
|
if (val > 0x7FFFFFFF) {
|
||||||
|
setType(tNumber);
|
||||||
|
number_ = static_cast<double>(val);
|
||||||
|
} else {
|
||||||
|
setType(tInteger);
|
||||||
|
int_ = static_cast<int>(val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Value::Value(sint64 val)
|
||||||
|
: type_(tUndefined)
|
||||||
|
{
|
||||||
|
if (val > 0x7FFFFFFFLL || val < -0x80000000LL) {
|
||||||
|
setType(tNumber);
|
||||||
|
number_ = static_cast<double>(val);
|
||||||
|
} else {
|
||||||
|
setType(tInteger);
|
||||||
|
int_ = static_cast<int>(val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Value::Value(uint64 val)
|
||||||
|
: type_(tUndefined)
|
||||||
|
{
|
||||||
|
if (val > 0x7FFFFFFFULL) {
|
||||||
|
setType(tNumber);
|
||||||
|
number_ = static_cast<double>(val);
|
||||||
|
} else {
|
||||||
|
setType(tInteger);
|
||||||
|
int_ = static_cast<int>(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<std::string>::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<int>(number_);
|
||||||
|
default: return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
double Value::getNumber() const {
|
||||||
|
switch (type_) {
|
||||||
|
case tInteger: return static_cast<double>(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<Map>::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_);
|
||||||
|
}
|
||||||
|
Value const& Value::operator[](char const* name) const {
|
||||||
|
Value const* ptr = get(name);
|
||||||
|
return (ptr ? *ptr : Defaults<Value>::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<Array>::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<uint32>(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>::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<Value::Type> 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<uint8>(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;
|
||||||
|
|
||||||
|
}
|
||||||
381
src/base/json.h
Normal file
381
src/base/json.h
Normal file
@ -0,0 +1,381 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "types.h"
|
||||||
|
#include "file.h"
|
||||||
|
#include "error.h"
|
||||||
|
#include <vector>
|
||||||
|
#include <map>
|
||||||
|
|
||||||
|
namespace json {
|
||||||
|
|
||||||
|
class Visitor;
|
||||||
|
|
||||||
|
class Value {
|
||||||
|
public:
|
||||||
|
typedef std::map<std::string, Value> Map;
|
||||||
|
typedef std::vector<Value> 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<std::string, Value> Map;
|
||||||
|
typedef std::vector<Value> 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<Value*> stack_;
|
||||||
|
enum {
|
||||||
|
sStart,
|
||||||
|
sMapValue,
|
||||||
|
sMapKey,
|
||||||
|
sArrayValue,
|
||||||
|
sFinish,
|
||||||
|
} state_;
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
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);
|
||||||
|
|
||||||
|
}
|
||||||
91
src/base/path.cpp
Normal file
91
src/base/path.cpp
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
#include "path.h"
|
||||||
|
#include <windows.h>
|
||||||
|
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 <windows.h>
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
17
src/base/path.h
Normal file
17
src/base/path.h
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
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);
|
||||||
100
src/base/pool.cpp
Normal file
100
src/base/pool.cpp
Normal file
@ -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;
|
||||||
|
}
|
||||||
55
src/base/pool.h
Normal file
55
src/base/pool.h
Normal file
@ -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 T>
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
};
|
||||||
837
src/base/regexp.cpp
Normal file
837
src/base/regexp.cpp
Normal file
@ -0,0 +1,837 @@
|
|||||||
|
#include <string.h>
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
#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<std::vector<std::string>*>(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<std::string>* 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<std::string>* 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<Match> matches;
|
||||||
|
std::vector<std::string>* 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<std::string> Prog::findAll(char const* text) {
|
||||||
|
std::vector<std::string> 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<Match> 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();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
163
src/base/regexp.h
Normal file
163
src/base/regexp.h
Normal file
@ -0,0 +1,163 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "types.h"
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace re {
|
||||||
|
|
||||||
|
typedef bool(*CharTraitFunc)(uint32);
|
||||||
|
class CharacterClass {
|
||||||
|
bool invert;
|
||||||
|
std::vector<CharTraitFunc> 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<Range> 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<CharacterClass*> 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 Func>
|
||||||
|
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 Func>
|
||||||
|
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<std::string>* sub = NULL);
|
||||||
|
int find(char const* text, int start = 0, std::vector<std::string>* sub = NULL);
|
||||||
|
|
||||||
|
bool match(std::string const& text, std::vector<std::string>* sub = NULL) {
|
||||||
|
return match(text.c_str(), sub);
|
||||||
|
}
|
||||||
|
int find(std::string const& text, int start = 0, std::vector<std::string>* sub = NULL) {
|
||||||
|
return find(text.c_str(), start, sub);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::string> findAll(char const* text);
|
||||||
|
template<class Func>
|
||||||
|
void findAll(char const* text, Func const& func) {
|
||||||
|
findAll_(text, &FindFuncHolder<Func>(func));
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::string> findAll(std::string const& text) {
|
||||||
|
return findAll(text.c_str());
|
||||||
|
}
|
||||||
|
template<class Func>
|
||||||
|
void findAll(std::string const& text, Func const& func) {
|
||||||
|
findAll_(text.c_str(), &FindFuncHolder<Func>(func));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class Func>
|
||||||
|
std::string replace(char const* text, Func const& func) {
|
||||||
|
return replace_(text, &ReplaceFuncHolder<Func>(func));
|
||||||
|
}
|
||||||
|
std::string replace(char const* text, char const* with);
|
||||||
|
|
||||||
|
template<class Func>
|
||||||
|
std::string replace(std::string const& text, Func const& func) {
|
||||||
|
return replace_(text.c_str(), &ReplaceFuncHolder<Func>(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);
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
165
src/base/string.cpp
Normal file
165
src/base/string.cpp
Normal file
@ -0,0 +1,165 @@
|
|||||||
|
#include "string.h"
|
||||||
|
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <clocale>
|
||||||
|
#include <algorithm>
|
||||||
|
#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<std::string> split(std::string const& str, char sep) {
|
||||||
|
std::vector<std::string> 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<std::string> split_multiple(std::string const& str, char const* sep) {
|
||||||
|
std::vector<std::string> 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<std::string> split(std::string const& str, char const* sep) {
|
||||||
|
size_t sep_length = strlen(sep);
|
||||||
|
std::vector<std::string> 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<std::string> 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<std::string> 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);
|
||||||
|
}
|
||||||
77
src/base/string.h
Normal file
77
src/base/string.h
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <cctype>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
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<char, ci_char_traits> {
|
||||||
|
public:
|
||||||
|
typedef std::basic_string<char, ci_char_traits> _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<class Iter>
|
||||||
|
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<class T>
|
||||||
|
inline int basic_compare(T const& lhs, T const& rhs) {
|
||||||
|
if (lhs < rhs) return -1;
|
||||||
|
if (lhs > rhs) return 1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::string> split(std::string const& str, char sep = ' ');
|
||||||
|
std::vector<std::string> split(std::string const& str, char const* sep);
|
||||||
|
std::vector<std::string> split_multiple(std::string const& str, char const* sep);
|
||||||
|
std::string join(std::vector<std::string> const& list, char sep = ' ');
|
||||||
|
std::string join(std::vector<std::string> const& list, std::string const& sep);
|
||||||
|
template<class Iter>
|
||||||
|
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);
|
||||||
56
src/base/thread.cpp
Normal file
56
src/base/thread.cpp
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
#include "thread.h"
|
||||||
|
#include <windows.h>
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
102
src/base/thread.h
Normal file
102
src/base/thread.h
Normal file
@ -0,0 +1,102 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <windows.h>
|
||||||
|
|
||||||
|
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<class T>
|
||||||
|
struct ThreadArg {
|
||||||
|
T* ptr;
|
||||||
|
int (T::*func)();
|
||||||
|
};
|
||||||
|
template<class T>
|
||||||
|
DWORD WINAPI ThreadProc(LPVOID arg) {
|
||||||
|
ThreadArg<T>* ta = (ThreadArg<T>*) arg;
|
||||||
|
int result = (ta->ptr->*(ta->func))();
|
||||||
|
delete ta;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
template<class T>
|
||||||
|
HANDLE create(T* ptr, int (T::*func)(), unsigned long* id = nullptr) {
|
||||||
|
ThreadArg<T>* ta = new ThreadArg<T>;
|
||||||
|
ta->ptr = ptr;
|
||||||
|
ta->func = func;
|
||||||
|
return CreateThread(NULL, 0, ThreadProc<T>, ta, 0, id);
|
||||||
|
}
|
||||||
|
template<class T, class A>
|
||||||
|
struct ThreadArg1 {
|
||||||
|
T* ptr;
|
||||||
|
int (T::*func)(A);
|
||||||
|
A arg;
|
||||||
|
};
|
||||||
|
template<class T, class A>
|
||||||
|
DWORD WINAPI ThreadProc1(LPVOID arg) {
|
||||||
|
ThreadArg1<T, A>* ta = (ThreadArg1<T, A>*) arg;
|
||||||
|
int result = (ta->ptr->*(ta->func))(ta->arg);
|
||||||
|
delete ta;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
template<class T, class A>
|
||||||
|
HANDLE create(T* ptr, int (T::*func)(A), A arg, unsigned long* id = nullptr) {
|
||||||
|
ThreadArg1<T, A>* ta = new ThreadArg1<T, A>;
|
||||||
|
ta->ptr = ptr;
|
||||||
|
ta->func = func;
|
||||||
|
ta->arg = arg;
|
||||||
|
return CreateThread(NULL, 0, ThreadProc1<T, A>, ta, 0, id);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
60
src/base/types.h
Normal file
60
src/base/types.h
Normal file
@ -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<int N> 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<int N> using UInt = typename UIntTraits<N>::Type;
|
||||||
1176
src/base/utf8.cpp
Normal file
1176
src/base/utf8.cpp
Normal file
File diff suppressed because it is too large
Load Diff
18
src/base/utf8.h
Normal file
18
src/base/utf8.h
Normal file
@ -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);
|
||||||
|
|
||||||
|
}
|
||||||
597
src/data.cpp
Normal file
597
src/data.cpp
Normal file
@ -0,0 +1,597 @@
|
|||||||
|
#define NOMINMAX
|
||||||
|
#include "base/http.h"
|
||||||
|
#include "data.h"
|
||||||
|
#include "base/thread.h"
|
||||||
|
#include "app.h"
|
||||||
|
#include "wizard.h"
|
||||||
|
#include <algorithm>
|
||||||
|
#include <time.h>
|
||||||
|
|
||||||
|
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::Tag> ProgramData::loadTags(File& file, uint32 numTags, uint32 numEntries) {
|
||||||
|
std::vector<Tag> 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<std::vector<std::string>> 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<uint8> ProgramData::downloadMask() {
|
||||||
|
size_t maskSize = (file_sizes_.size() + 7) / 8;
|
||||||
|
std::vector<uint8> mask(maskSize, 0);
|
||||||
|
for (std::string const& tags : used_tags) {
|
||||||
|
std::vector<uint8> 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<std::string> 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<uint8> mask;
|
||||||
|
};
|
||||||
|
enum { BlockSize = (1 << 20) };
|
||||||
|
std::vector<ArchiveInfo> archives_;
|
||||||
|
std::unordered_map<NGDP::Hash_container, IndexEntry, NGDP::Hash_container::hash, NGDP::Hash_container::equal> index_;
|
||||||
|
|
||||||
|
void loadIndices(NGDP::CascStorage& storage) {
|
||||||
|
NGDP::Hash nilHash;
|
||||||
|
memset(nilHash, 0, sizeof nilHash);
|
||||||
|
|
||||||
|
std::vector<std::string> 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));
|
||||||
|
}
|
||||||
126
src/data.h
Normal file
126
src/data.h
Normal file
@ -0,0 +1,126 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#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::NGDP> ngdp() {
|
||||||
|
return ngdp_;
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
std::shared_ptr<NGDP::NGDP> ngdp_;
|
||||||
|
|
||||||
|
// step 2
|
||||||
|
|
||||||
|
public:
|
||||||
|
void loadBuilds();
|
||||||
|
NGDP::ConfigFile cdn_config;
|
||||||
|
std::vector<std::string> builds;
|
||||||
|
std::map<std::string, NGDP::ConfigFile> build_configs;
|
||||||
|
std::string builds_loaded;
|
||||||
|
std::string selected_build;
|
||||||
|
|
||||||
|
// step 3
|
||||||
|
|
||||||
|
public:
|
||||||
|
void loadTags();
|
||||||
|
std::vector<std::vector<std::string>> tags;
|
||||||
|
std::vector<std::string> used_tags;
|
||||||
|
uint64 downloadSize();
|
||||||
|
|
||||||
|
private:
|
||||||
|
struct Tag {
|
||||||
|
std::string name;
|
||||||
|
uint16 type;
|
||||||
|
std::vector<uint8> mask;
|
||||||
|
};
|
||||||
|
|
||||||
|
std::shared_ptr<NGDP::Encoding> encoding_;
|
||||||
|
std::vector<uint32> file_sizes_;
|
||||||
|
std::vector<Tag> tags_;
|
||||||
|
|
||||||
|
static std::vector<Tag> loadTags(File& file, uint32 numTags, uint32 numEntries);
|
||||||
|
std::vector<uint8> downloadMask();
|
||||||
|
|
||||||
|
// step 4
|
||||||
|
|
||||||
|
public:
|
||||||
|
struct Progress {
|
||||||
|
std::vector<std::string> log;
|
||||||
|
|
||||||
|
uint32 filesTotal = 0;
|
||||||
|
uint32 filesDone = 0;
|
||||||
|
uint64 sizeTotal = 0;
|
||||||
|
uint64 sizeDone = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
void download(std::string const& path);
|
||||||
|
std::shared_ptr<Progress> progress() {
|
||||||
|
return progress_;
|
||||||
|
}
|
||||||
|
void stopDownload() {
|
||||||
|
stop();
|
||||||
|
progress_.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::shared_ptr<Progress> 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> task_;
|
||||||
|
void setTask(Task* task);
|
||||||
|
};
|
||||||
650
src/frameui/controlframes.cpp
Normal file
650
src/frameui/controlframes.cpp
Normal file
@ -0,0 +1,650 @@
|
|||||||
|
#define NOMINMAX
|
||||||
|
#include <windows.h>
|
||||||
|
#include <windowsx.h>
|
||||||
|
|
||||||
|
#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<char*>(&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<char*>(&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;
|
||||||
|
}
|
||||||
216
src/frameui/controlframes.h
Normal file
216
src/frameui/controlframes.h
Normal file
@ -0,0 +1,216 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "frameui/frame.h"
|
||||||
|
#include "frameui/window.h"
|
||||||
|
#include "frameui/framewnd.h"
|
||||||
|
#include <CommCtrl.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;
|
||||||
|
};
|
||||||
|
|
||||||
|
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<Frame*> 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);
|
||||||
|
};
|
||||||
171
src/frameui/fontsys.cpp
Normal file
171
src/frameui/fontsys.cpp
Normal file
@ -0,0 +1,171 @@
|
|||||||
|
#include "fontsys.h"
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
64
src/frameui/fontsys.h
Normal file
64
src/frameui/fontsys.h
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <windows.h>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
#include <map>
|
||||||
|
#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<FontStruct> fonts;
|
||||||
|
int logPixelsY;
|
||||||
|
HDC hDC;
|
||||||
|
FontSys();
|
||||||
|
std::map<std::string, HFONT> 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);
|
||||||
|
};
|
||||||
436
src/frameui/frame.cpp
Normal file
436
src/frameui/frame.cpp
Normal file
@ -0,0 +1,436 @@
|
|||||||
|
#include <stdlib.h>
|
||||||
|
#include <memory.h>
|
||||||
|
#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<Frame*> 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////
|
||||||
158
src/frameui/frame.h
Normal file
158
src/frameui/frame.h
Normal file
@ -0,0 +1,158 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <windows.h>
|
||||||
|
#include <vector>
|
||||||
|
#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<Frame*> 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);
|
||||||
|
};
|
||||||
209
src/frameui/framewnd.cpp
Normal file
209
src/frameui/framewnd.cpp
Normal file
@ -0,0 +1,209 @@
|
|||||||
|
#include <windows.h>
|
||||||
|
|
||||||
|
#include "framewnd.h"
|
||||||
|
|
||||||
|
WindowFrame::WindowFrame(Frame* parent)
|
||||||
|
: Frame(parent)
|
||||||
|
{
|
||||||
|
Frame* cur = getParent();
|
||||||
|
while (cur->getParent()) cur = cur->getParent();
|
||||||
|
RootWindow* frm = dynamic_cast<RootWindow*>(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<WindowFrame*>(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<RootWindow*>(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<WindowFrame*>(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<RootWindow*>(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);
|
||||||
|
}
|
||||||
40
src/frameui/framewnd.h
Normal file
40
src/frameui/framewnd.h
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <windows.h>
|
||||||
|
#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);
|
||||||
|
};
|
||||||
39
src/frameui/listctrl.cpp
Normal file
39
src/frameui/listctrl.cpp
Normal file
@ -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<char*>(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;
|
||||||
|
}
|
||||||
24
src/frameui/listctrl.h
Normal file
24
src/frameui/listctrl.h
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "frameui/frame.h"
|
||||||
|
#include "frameui/window.h"
|
||||||
|
#include "frameui/framewnd.h"
|
||||||
|
|
||||||
|
#include <commctrl.h>
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
};
|
||||||
474
src/frameui/searchlist.cpp
Normal file
474
src/frameui/searchlist.cpp
Normal file
@ -0,0 +1,474 @@
|
|||||||
|
#include "searchlist.h"
|
||||||
|
#include "fontsys.h"
|
||||||
|
#include <windowsx.h>
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
////////////////////////////////////////
|
||||||
|
|
||||||
|
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<size_t> 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<size_t> 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);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//}
|
||||||
19
src/frameui/searchlist.h
Normal file
19
src/frameui/searchlist.h
Normal file
@ -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;
|
||||||
|
};
|
||||||
335
src/frameui/window.cpp
Normal file
335
src/frameui/window.cpp
Normal file
@ -0,0 +1,335 @@
|
|||||||
|
#include "window.h"
|
||||||
|
|
||||||
|
#include "frameui/fontsys.h"
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <windowsx.h>
|
||||||
|
|
||||||
|
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<HWND, Window*> 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
77
src/frameui/window.h
Normal file
77
src/frameui/window.h
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <windows.h>
|
||||||
|
#include <map>
|
||||||
|
#include "base/types.h"
|
||||||
|
|
||||||
|
class Window
|
||||||
|
{
|
||||||
|
static LRESULT CALLBACK WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
|
||||||
|
static ATOM windowClass;
|
||||||
|
static std::map<HWND, Window*> 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);
|
||||||
|
};
|
||||||
23
src/main.cpp
Normal file
23
src/main.cpp
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
#include "app.h"
|
||||||
|
#include <windows.h>
|
||||||
|
#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;
|
||||||
|
}
|
||||||
|
}
|
||||||
457
src/ngdp.cpp
Normal file
457
src/ngdp.cpp
Normal file
@ -0,0 +1,457 @@
|
|||||||
|
#include "ngdp.h"
|
||||||
|
#include "base/http.h"
|
||||||
|
#include "base/path.h"
|
||||||
|
#include "base/checksum.h"
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
namespace NGDP {
|
||||||
|
|
||||||
|
const std::string HOST = "http://cn.patch.battle.net:1119";
|
||||||
|
|
||||||
|
const std::map<std::string, std::string> 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<uint32> csize;
|
||||||
|
std::vector<uint32> 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<uint8> 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<uint8> 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<EncodingEntry*>(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<LayoutEntry*>(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<class Vec, class Comp>
|
||||||
|
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<std::string> 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();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
182
src/ngdp.h
Normal file
182
src/ngdp.h
Normal file
@ -0,0 +1,182 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "base/common.h"
|
||||||
|
#include "base/json.h"
|
||||||
|
#include "base/file.h"
|
||||||
|
#include <unordered_map>
|
||||||
|
|
||||||
|
namespace NGDP {
|
||||||
|
|
||||||
|
extern const std::string HOST;
|
||||||
|
|
||||||
|
extern const std::map<std::string, std::string> 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<size_t const*>(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_container const*>(hash);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct CdnData {
|
||||||
|
std::string path;
|
||||||
|
std::vector<std::string> 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<std::string> 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<std::string, CdnData> cdns_;
|
||||||
|
std::map<std::string, VersionData> versions_;
|
||||||
|
std::vector<std::string> regions_;
|
||||||
|
std::string base_;
|
||||||
|
};
|
||||||
|
|
||||||
|
File DecodeBLTE(File& blte, uint32 usize = 0);
|
||||||
|
typedef std::map<std::string, std::string> 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<uint8> data_;
|
||||||
|
struct EncodingHeader {
|
||||||
|
Hash hash;
|
||||||
|
std::vector<EncodingEntry*> entries;
|
||||||
|
};
|
||||||
|
struct LayoutHeader {
|
||||||
|
Hash key;
|
||||||
|
std::vector<LayoutEntry*> entries;
|
||||||
|
};
|
||||||
|
std::vector<EncodingHeader> encodingTable_;
|
||||||
|
std::vector<LayoutHeader> layoutTable_;
|
||||||
|
std::vector<char*> 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<IndexEntry> index_;
|
||||||
|
File data_;
|
||||||
|
uint32 indexCount_;
|
||||||
|
uint32 dataCount_;
|
||||||
|
|
||||||
|
void writeIndex();
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
119
src/pages/build.cpp
Normal file
119
src/pages/build.cpp
Normal file
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
19
src/pages/build.h
Normal file
19
src/pages/build.h
Normal file
@ -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);
|
||||||
|
};
|
||||||
178
src/pages/download.cpp
Normal file
178
src/pages/download.cpp
Normal file
@ -0,0 +1,178 @@
|
|||||||
|
#include "download.h"
|
||||||
|
#include "ngdp.h"
|
||||||
|
#include "app.h"
|
||||||
|
#include "tags.h"
|
||||||
|
#include "frameui/fontsys.h"
|
||||||
|
#include <algorithm>
|
||||||
|
#include <shlobj.h>
|
||||||
|
|
||||||
|
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_);
|
||||||
|
}
|
||||||
28
src/pages/download.h
Normal file
28
src/pages/download.h
Normal file
@ -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);
|
||||||
|
};
|
||||||
115
src/pages/program.cpp
Normal file
115
src/pages/program.cpp
Normal file
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
20
src/pages/program.h
Normal file
20
src/pages/program.h
Normal file
@ -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<std::string> programs_;
|
||||||
|
std::vector<std::string> regions_;
|
||||||
|
};
|
||||||
153
src/pages/tags.cpp
Normal file
153
src/pages/tags.cpp
Normal file
@ -0,0 +1,153 @@
|
|||||||
|
#include "tags.h"
|
||||||
|
#include "ngdp.h"
|
||||||
|
#include "app.h"
|
||||||
|
#include "build.h"
|
||||||
|
#include "download.h"
|
||||||
|
#include "frameui/fontsys.h"
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
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<int>(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<int>(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_);
|
||||||
|
}
|
||||||
25
src/pages/tags.h
Normal file
25
src/pages/tags.h
Normal file
@ -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<ComboFrame*> options_;
|
||||||
|
ListFrame* tags_;
|
||||||
|
void init();
|
||||||
|
LRESULT onMessage(uint32 message, WPARAM wParam, LPARAM lParam);
|
||||||
|
};
|
||||||
80
src/wizard.cpp
Normal file
80
src/wizard.cpp
Normal file
@ -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;
|
||||||
|
}
|
||||||
55
src/wizard.h
Normal file
55
src/wizard.h
Normal file
@ -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_;
|
||||||
|
};
|
||||||
BIN
vc13/Battle_130.ico
Normal file
BIN
vc13/Battle_130.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 93 KiB |
BIN
vc13/BlizzGet.rc
Normal file
BIN
vc13/BlizzGet.rc
Normal file
Binary file not shown.
22
vc13/BlizzGet.sln
Normal file
22
vc13/BlizzGet.sln
Normal file
@ -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
|
||||||
155
vc13/BlizzGet.vcxproj
Normal file
155
vc13/BlizzGet.vcxproj
Normal file
@ -0,0 +1,155 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
|
<ItemGroup Label="ProjectConfigurations">
|
||||||
|
<ProjectConfiguration Include="Debug|Win32">
|
||||||
|
<Configuration>Debug</Configuration>
|
||||||
|
<Platform>Win32</Platform>
|
||||||
|
</ProjectConfiguration>
|
||||||
|
<ProjectConfiguration Include="Release|Win32">
|
||||||
|
<Configuration>Release</Configuration>
|
||||||
|
<Platform>Win32</Platform>
|
||||||
|
</ProjectConfiguration>
|
||||||
|
</ItemGroup>
|
||||||
|
<PropertyGroup Label="Globals">
|
||||||
|
<ProjectGuid>{5C7B85D1-E985-4331-862E-B4864324DCB5}</ProjectGuid>
|
||||||
|
<Keyword>Win32Proj</Keyword>
|
||||||
|
<RootNamespace>BlizzGet</RootNamespace>
|
||||||
|
</PropertyGroup>
|
||||||
|
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
|
||||||
|
<ConfigurationType>Application</ConfigurationType>
|
||||||
|
<UseDebugLibraries>true</UseDebugLibraries>
|
||||||
|
<PlatformToolset>v120</PlatformToolset>
|
||||||
|
<CharacterSet>MultiByte</CharacterSet>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
|
||||||
|
<ConfigurationType>Application</ConfigurationType>
|
||||||
|
<UseDebugLibraries>false</UseDebugLibraries>
|
||||||
|
<PlatformToolset>v120</PlatformToolset>
|
||||||
|
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||||
|
<CharacterSet>MultiByte</CharacterSet>
|
||||||
|
</PropertyGroup>
|
||||||
|
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||||
|
<ImportGroup Label="ExtensionSettings">
|
||||||
|
</ImportGroup>
|
||||||
|
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||||
|
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||||
|
</ImportGroup>
|
||||||
|
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||||
|
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||||
|
</ImportGroup>
|
||||||
|
<PropertyGroup Label="UserMacros" />
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||||
|
<LinkIncremental>true</LinkIncremental>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||||
|
<LinkIncremental>false</LinkIncremental>
|
||||||
|
</PropertyGroup>
|
||||||
|
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||||
|
<ClCompile>
|
||||||
|
<PrecompiledHeader>
|
||||||
|
</PrecompiledHeader>
|
||||||
|
<WarningLevel>Level2</WarningLevel>
|
||||||
|
<Optimization>Disabled</Optimization>
|
||||||
|
<PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||||
|
<AdditionalIncludeDirectories>../libs;../src;./</AdditionalIncludeDirectories>
|
||||||
|
</ClCompile>
|
||||||
|
<Link>
|
||||||
|
<SubSystem>Windows</SubSystem>
|
||||||
|
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||||
|
<AdditionalLibraryDirectories>../libs</AdditionalLibraryDirectories>
|
||||||
|
<AdditionalDependencies>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)</AdditionalDependencies>
|
||||||
|
</Link>
|
||||||
|
</ItemDefinitionGroup>
|
||||||
|
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||||
|
<ClCompile>
|
||||||
|
<WarningLevel>Level2</WarningLevel>
|
||||||
|
<PrecompiledHeader>
|
||||||
|
</PrecompiledHeader>
|
||||||
|
<Optimization>MaxSpeed</Optimization>
|
||||||
|
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||||
|
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||||
|
<PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||||
|
<AdditionalIncludeDirectories>./;../libs/;../src</AdditionalIncludeDirectories>
|
||||||
|
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
|
||||||
|
</ClCompile>
|
||||||
|
<Link>
|
||||||
|
<SubSystem>Windows</SubSystem>
|
||||||
|
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||||
|
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||||
|
<OptimizeReferences>true</OptimizeReferences>
|
||||||
|
<AdditionalLibraryDirectories>../libs</AdditionalLibraryDirectories>
|
||||||
|
<AdditionalDependencies>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)</AdditionalDependencies>
|
||||||
|
<IgnoreSpecificDefaultLibraries>libcmt.lib</IgnoreSpecificDefaultLibraries>
|
||||||
|
</Link>
|
||||||
|
</ItemDefinitionGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<ClCompile Include="..\src\app.cpp" />
|
||||||
|
<ClCompile Include="..\src\base\checksum.cpp" />
|
||||||
|
<ClCompile Include="..\src\base\common.cpp" />
|
||||||
|
<ClCompile Include="..\src\base\error.cpp" />
|
||||||
|
<ClCompile Include="..\src\base\file.cpp" />
|
||||||
|
<ClCompile Include="..\src\base\http.cpp" />
|
||||||
|
<ClCompile Include="..\src\base\json.cpp" />
|
||||||
|
<ClCompile Include="..\src\base\path.cpp" />
|
||||||
|
<ClCompile Include="..\src\base\pool.cpp" />
|
||||||
|
<ClCompile Include="..\src\base\regexp.cpp" />
|
||||||
|
<ClCompile Include="..\src\base\string.cpp" />
|
||||||
|
<ClCompile Include="..\src\base\thread.cpp" />
|
||||||
|
<ClCompile Include="..\src\base\utf8.cpp" />
|
||||||
|
<ClCompile Include="..\src\data.cpp" />
|
||||||
|
<ClCompile Include="..\src\frameui\controlframes.cpp" />
|
||||||
|
<ClCompile Include="..\src\frameui\fontsys.cpp" />
|
||||||
|
<ClCompile Include="..\src\frameui\frame.cpp" />
|
||||||
|
<ClCompile Include="..\src\frameui\framewnd.cpp" />
|
||||||
|
<ClCompile Include="..\src\frameui\listctrl.cpp" />
|
||||||
|
<ClCompile Include="..\src\frameui\window.cpp" />
|
||||||
|
<ClCompile Include="..\src\main.cpp" />
|
||||||
|
<ClCompile Include="..\src\ngdp.cpp" />
|
||||||
|
<ClCompile Include="..\src\pages\build.cpp" />
|
||||||
|
<ClCompile Include="..\src\pages\download.cpp" />
|
||||||
|
<ClCompile Include="..\src\pages\program.cpp" />
|
||||||
|
<ClCompile Include="..\src\pages\tags.cpp" />
|
||||||
|
<ClCompile Include="..\src\wizard.cpp" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<ClInclude Include="..\src\app.h" />
|
||||||
|
<ClInclude Include="..\src\base\checksum.h" />
|
||||||
|
<ClInclude Include="..\src\base\common.h" />
|
||||||
|
<ClInclude Include="..\src\base\error.h" />
|
||||||
|
<ClInclude Include="..\src\base\file.h" />
|
||||||
|
<ClInclude Include="..\src\base\functor.h" />
|
||||||
|
<ClInclude Include="..\src\base\http.h" />
|
||||||
|
<ClInclude Include="..\src\base\json.h" />
|
||||||
|
<ClInclude Include="..\src\base\path.h" />
|
||||||
|
<ClInclude Include="..\src\base\pool.h" />
|
||||||
|
<ClInclude Include="..\src\base\regexp.h" />
|
||||||
|
<ClInclude Include="..\src\base\string.h" />
|
||||||
|
<ClInclude Include="..\src\base\thread.h" />
|
||||||
|
<ClInclude Include="..\src\base\types.h" />
|
||||||
|
<ClInclude Include="..\src\base\utf8.h" />
|
||||||
|
<ClInclude Include="..\src\frameui\controlframes.h" />
|
||||||
|
<ClInclude Include="..\src\frameui\fontsys.h" />
|
||||||
|
<ClInclude Include="..\src\frameui\frame.h" />
|
||||||
|
<ClInclude Include="..\src\frameui\framewnd.h" />
|
||||||
|
<ClInclude Include="..\src\frameui\listctrl.h" />
|
||||||
|
<ClInclude Include="..\src\frameui\window.h" />
|
||||||
|
<ClInclude Include="..\src\ngdp.h" />
|
||||||
|
<ClInclude Include="..\src\pages\build.h" />
|
||||||
|
<ClInclude Include="..\src\pages\download.h" />
|
||||||
|
<ClInclude Include="..\src\pages\program.h" />
|
||||||
|
<ClInclude Include="..\src\data.h" />
|
||||||
|
<ClInclude Include="..\src\pages\tags.h" />
|
||||||
|
<ClInclude Include="..\src\wizard.h" />
|
||||||
|
<ClInclude Include="resource.h" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<ResourceCompile Include="BlizzGet.rc" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Image Include="Battle_130.ico" />
|
||||||
|
</ItemGroup>
|
||||||
|
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||||
|
<ImportGroup Label="ExtensionTargets">
|
||||||
|
</ImportGroup>
|
||||||
|
</Project>
|
||||||
226
vc13/BlizzGet.vcxproj.filters
Normal file
226
vc13/BlizzGet.vcxproj.filters
Normal file
@ -0,0 +1,226 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
|
<ItemGroup>
|
||||||
|
<Filter Include="Source Files">
|
||||||
|
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
|
||||||
|
<Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
|
||||||
|
</Filter>
|
||||||
|
<Filter Include="Header Files">
|
||||||
|
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
|
||||||
|
<Extensions>h;hh;hpp;hxx;hm;inl;inc;xsd</Extensions>
|
||||||
|
</Filter>
|
||||||
|
<Filter Include="Resource Files">
|
||||||
|
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
|
||||||
|
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
|
||||||
|
</Filter>
|
||||||
|
<Filter Include="Base">
|
||||||
|
<UniqueIdentifier>{49b335fb-4d74-4e3a-bd5a-30213f6bc659}</UniqueIdentifier>
|
||||||
|
</Filter>
|
||||||
|
<Filter Include="Base\Header Files">
|
||||||
|
<UniqueIdentifier>{77292007-2477-4c3b-8cf3-188ebcd657fb}</UniqueIdentifier>
|
||||||
|
</Filter>
|
||||||
|
<Filter Include="Base\Source Files">
|
||||||
|
<UniqueIdentifier>{21d34fab-6962-4f59-9819-d2078d91e4a7}</UniqueIdentifier>
|
||||||
|
</Filter>
|
||||||
|
<Filter Include="FrameUI">
|
||||||
|
<UniqueIdentifier>{a3f7604a-19e5-4519-98cd-d883b7400bac}</UniqueIdentifier>
|
||||||
|
</Filter>
|
||||||
|
<Filter Include="FrameUI\Header Files">
|
||||||
|
<UniqueIdentifier>{7fbda502-e098-492a-a874-85590a929e03}</UniqueIdentifier>
|
||||||
|
</Filter>
|
||||||
|
<Filter Include="FrameUI\Source Files">
|
||||||
|
<UniqueIdentifier>{ca7a40cf-b104-4d1b-bee4-a3b11be5f813}</UniqueIdentifier>
|
||||||
|
</Filter>
|
||||||
|
<Filter Include="Pages">
|
||||||
|
<UniqueIdentifier>{5bf51ea9-d4c9-43e0-aed6-ef53f22e288d}</UniqueIdentifier>
|
||||||
|
</Filter>
|
||||||
|
<Filter Include="Pages\Header Files">
|
||||||
|
<UniqueIdentifier>{7f32c3d0-bdb8-48e1-b2ff-a6f827929e83}</UniqueIdentifier>
|
||||||
|
</Filter>
|
||||||
|
<Filter Include="Pages\Source Files">
|
||||||
|
<UniqueIdentifier>{4f824606-2631-4807-a689-de2b0ebe5350}</UniqueIdentifier>
|
||||||
|
</Filter>
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<ClCompile Include="..\src\base\checksum.cpp">
|
||||||
|
<Filter>Base\Source Files</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="..\src\base\common.cpp">
|
||||||
|
<Filter>Base\Source Files</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="..\src\base\error.cpp">
|
||||||
|
<Filter>Base\Source Files</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="..\src\base\file.cpp">
|
||||||
|
<Filter>Base\Source Files</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="..\src\base\http.cpp">
|
||||||
|
<Filter>Base\Source Files</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="..\src\base\json.cpp">
|
||||||
|
<Filter>Base\Source Files</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="..\src\base\path.cpp">
|
||||||
|
<Filter>Base\Source Files</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="..\src\base\pool.cpp">
|
||||||
|
<Filter>Base\Source Files</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="..\src\base\regexp.cpp">
|
||||||
|
<Filter>Base\Source Files</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="..\src\base\string.cpp">
|
||||||
|
<Filter>Base\Source Files</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="..\src\base\thread.cpp">
|
||||||
|
<Filter>Base\Source Files</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="..\src\base\utf8.cpp">
|
||||||
|
<Filter>Base\Source Files</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="..\src\frameui\controlframes.cpp">
|
||||||
|
<Filter>FrameUI\Source Files</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="..\src\frameui\fontsys.cpp">
|
||||||
|
<Filter>FrameUI\Source Files</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="..\src\frameui\frame.cpp">
|
||||||
|
<Filter>FrameUI\Source Files</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="..\src\frameui\framewnd.cpp">
|
||||||
|
<Filter>FrameUI\Source Files</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="..\src\frameui\window.cpp">
|
||||||
|
<Filter>FrameUI\Source Files</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="..\src\main.cpp">
|
||||||
|
<Filter>Source Files</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="..\src\app.cpp">
|
||||||
|
<Filter>Source Files</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="..\src\ngdp.cpp">
|
||||||
|
<Filter>Source Files</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="..\src\wizard.cpp">
|
||||||
|
<Filter>Source Files</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="..\src\pages\program.cpp">
|
||||||
|
<Filter>Pages\Source Files</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="..\src\data.cpp">
|
||||||
|
<Filter>Source Files</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="..\src\pages\build.cpp">
|
||||||
|
<Filter>Pages\Source Files</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="..\src\frameui\listctrl.cpp">
|
||||||
|
<Filter>FrameUI\Source Files</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="..\src\pages\tags.cpp">
|
||||||
|
<Filter>Pages\Source Files</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="..\src\pages\download.cpp">
|
||||||
|
<Filter>Pages\Source Files</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<ClInclude Include="..\src\base\checksum.h">
|
||||||
|
<Filter>Base\Header Files</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
<ClInclude Include="..\src\base\common.h">
|
||||||
|
<Filter>Base\Header Files</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
<ClInclude Include="..\src\base\error.h">
|
||||||
|
<Filter>Base\Header Files</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
<ClInclude Include="..\src\base\file.h">
|
||||||
|
<Filter>Base\Header Files</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
<ClInclude Include="..\src\base\functor.h">
|
||||||
|
<Filter>Base\Header Files</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
<ClInclude Include="..\src\base\http.h">
|
||||||
|
<Filter>Base\Header Files</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
<ClInclude Include="..\src\base\json.h">
|
||||||
|
<Filter>Base\Header Files</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
<ClInclude Include="..\src\base\path.h">
|
||||||
|
<Filter>Base\Header Files</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
<ClInclude Include="..\src\base\pool.h">
|
||||||
|
<Filter>Base\Header Files</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
<ClInclude Include="..\src\base\regexp.h">
|
||||||
|
<Filter>Base\Header Files</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
<ClInclude Include="..\src\base\string.h">
|
||||||
|
<Filter>Base\Header Files</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
<ClInclude Include="..\src\base\thread.h">
|
||||||
|
<Filter>Base\Header Files</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
<ClInclude Include="..\src\base\types.h">
|
||||||
|
<Filter>Base\Header Files</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
<ClInclude Include="..\src\base\utf8.h">
|
||||||
|
<Filter>Base\Header Files</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
<ClInclude Include="..\src\frameui\controlframes.h">
|
||||||
|
<Filter>FrameUI\Header Files</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
<ClInclude Include="..\src\frameui\fontsys.h">
|
||||||
|
<Filter>FrameUI\Header Files</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
<ClInclude Include="..\src\frameui\frame.h">
|
||||||
|
<Filter>FrameUI\Header Files</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
<ClInclude Include="..\src\frameui\framewnd.h">
|
||||||
|
<Filter>FrameUI\Header Files</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
<ClInclude Include="..\src\frameui\window.h">
|
||||||
|
<Filter>FrameUI\Header Files</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
<ClInclude Include="..\src\app.h">
|
||||||
|
<Filter>Header Files</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
<ClInclude Include="..\src\ngdp.h">
|
||||||
|
<Filter>Header Files</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
<ClInclude Include="..\src\wizard.h">
|
||||||
|
<Filter>Header Files</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
<ClInclude Include="resource.h">
|
||||||
|
<Filter>Header Files</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
<ClInclude Include="..\src\pages\program.h">
|
||||||
|
<Filter>Pages\Header Files</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
<ClInclude Include="..\src\data.h">
|
||||||
|
<Filter>Header Files</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
<ClInclude Include="..\src\pages\build.h">
|
||||||
|
<Filter>Pages\Header Files</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
<ClInclude Include="..\src\pages\tags.h">
|
||||||
|
<Filter>Pages\Header Files</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
<ClInclude Include="..\src\frameui\listctrl.h">
|
||||||
|
<Filter>FrameUI\Header Files</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
<ClInclude Include="..\src\pages\download.h">
|
||||||
|
<Filter>Pages\Header Files</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<ResourceCompile Include="BlizzGet.rc">
|
||||||
|
<Filter>Resource Files</Filter>
|
||||||
|
</ResourceCompile>
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Image Include="Battle_130.ico">
|
||||||
|
<Filter>Resource Files</Filter>
|
||||||
|
</Image>
|
||||||
|
</ItemGroup>
|
||||||
|
</Project>
|
||||||
BIN
vc13/resource.h
Normal file
BIN
vc13/resource.h
Normal file
Binary file not shown.
Loading…
Reference in New Issue
Block a user