mirror of
https://github.com/CDAGaming/blizzget
synced 2026-01-31 22:52:44 +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