initial release

This commit is contained in:
d07RiV 2016-03-09 11:42:26 +03:00
parent ca74d0450e
commit 348199501d
71 changed files with 13864 additions and 0 deletions

BIN
libs/zlib/vc100.pdb Normal file

Binary file not shown.

511
libs/zlib/zconf.h Normal file
View 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

File diff suppressed because it is too large Load Diff

BIN
libs/zlib/zlib32d.lib Normal file

Binary file not shown.

BIN
libs/zlib/zlib32r.lib Normal file

Binary file not shown.

BIN
libs/zlib/zlib64d.lib Normal file

Binary file not shown.

BIN
libs/zlib/zlib64r.lib Normal file

Binary file not shown.

253
libs/zlib/zutil.h Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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

File diff suppressed because it is too large Load Diff

18
src/base/utf8.h Normal file
View 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
View 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(&timestruct, &curtime);
strftime(timebuf, sizeof timebuf, "%Y-%m-%dT%H:%M:%SZ", &timestruct);
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
View 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);
};

View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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

Binary file not shown.

After

Width:  |  Height:  |  Size: 93 KiB

BIN
vc13/BlizzGet.rc Normal file

Binary file not shown.

22
vc13/BlizzGet.sln Normal file
View 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
View 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>

View 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

Binary file not shown.