From dbe1850ec10c3aadcafb68d2ca45b6eb46872a45 Mon Sep 17 00:00:00 2001 From: Kawe Mazidjatari <48657826+Mauler125@users.noreply.github.com> Date: Wed, 5 Apr 2023 21:38:28 +0200 Subject: [PATCH] Add LZSS implementation to SDK --- r5dev/public/tier0/memdbgon.h | 4 + r5dev/public/tier1/lzss.h | 71 +++++ r5dev/tier1/lzss.cpp | 425 ++++++++++++++++++++++++++ r5dev/vproj/clientsdk.vcxproj | 8 +- r5dev/vproj/clientsdk.vcxproj.filters | 12 + r5dev/vproj/dedicated.vcxproj | 8 +- r5dev/vproj/dedicated.vcxproj.filters | 12 + r5dev/vproj/gamesdk.vcxproj | 9 +- r5dev/vproj/gamesdk.vcxproj.filters | 15 + r5dev/vproj/sdklauncher.vcxproj | 6 +- 10 files changed, 558 insertions(+), 12 deletions(-) create mode 100644 r5dev/public/tier0/memdbgon.h create mode 100644 r5dev/public/tier1/lzss.h create mode 100644 r5dev/tier1/lzss.cpp diff --git a/r5dev/public/tier0/memdbgon.h b/r5dev/public/tier0/memdbgon.h new file mode 100644 index 00000000..bda6e94e --- /dev/null +++ b/r5dev/public/tier0/memdbgon.h @@ -0,0 +1,4 @@ +#ifndef MEMDBGON_H +#define MEMDBGON_H + +#endif // MEMDBGON_H diff --git a/r5dev/public/tier1/lzss.h b/r5dev/public/tier1/lzss.h new file mode 100644 index 00000000..86fbabeb --- /dev/null +++ b/r5dev/public/tier1/lzss.h @@ -0,0 +1,71 @@ +//========= Copyright © 1996-2007, Valve Corporation, All rights reserved. ============// +// +// LZSS Codec. Designed for fast cheap gametime encoding/decoding. Compression results +// are not aggresive as other alogrithms, but gets 2:1 on most arbitrary uncompressed data. +// +//=====================================================================================// + +#ifndef _LZSS_H +#define _LZSS_H +#pragma once + +#if defined( PLAT_LITTLE_ENDIAN ) +#define LZSS_ID (('S'<<24)|('S'<<16)|('Z'<<8)|('L')) +#else +#define LZSS_ID (('L'<<24)|('Z'<<16)|('S'<<8)|('S')) +#endif + +// bind the buffer for correct identification +struct lzss_header_t +{ + unsigned int id; + unsigned int actualSize; // always little endian +}; + +class CUtlBuffer; + +#define DEFAULT_LZSS_WINDOW_SIZE 4096 + +class CLZSS +{ +public: + unsigned char* Compress( unsigned char *pInput, int inputlen, unsigned int *pOutputSize ); + unsigned char* CompressNoAlloc( unsigned char *pInput, int inputlen, unsigned char *pOutput, unsigned int *pOutputSize ); + unsigned int Uncompress( unsigned char *pInput, unsigned char *pOutput ); + //unsigned int Uncompress( unsigned char *pInput, CUtlBuffer &buf ); + unsigned int SafeUncompress( unsigned char *pInput, unsigned char *pOutput, unsigned int unBufSize ); + bool IsCompressed( unsigned char *pInput ); + unsigned int GetActualSize( unsigned char *pInput ); + + // windowsize must be a power of two. + FORCEINLINE CLZSS( int nWindowSize = DEFAULT_LZSS_WINDOW_SIZE ); + +private: + // expected to be sixteen bytes + struct lzss_node_t + { + unsigned char *pData; + lzss_node_t *pPrev; + lzss_node_t *pNext; + char empty[4]; + }; + + struct lzss_list_t + { + lzss_node_t *pStart; + lzss_node_t *pEnd; + }; + + void BuildHash( unsigned char *pData ); + lzss_list_t *m_pHashTable; + lzss_node_t *m_pHashTarget; + int m_nWindowSize; + +}; + +FORCEINLINE CLZSS::CLZSS( int nWindowSize ) +{ + m_nWindowSize = nWindowSize; +} +#endif + diff --git a/r5dev/tier1/lzss.cpp b/r5dev/tier1/lzss.cpp new file mode 100644 index 00000000..4294f059 --- /dev/null +++ b/r5dev/tier1/lzss.cpp @@ -0,0 +1,425 @@ +//========= Copyright © 1996-2007, Valve Corporation, All rights reserved. ============// +// +// LZSS Codec. Designed for fast cheap gametime encoding/decoding. Compression results +// are not aggresive as other alogrithms, but gets 2:1 on most arbitrary uncompressed data. +// +//=====================================================================================// + +#include "core/stdafx.h" +#include "tier0/platform.h" +#include "tier0/dbg.h" +#include "tier1/lzss.h" +#include "tier1/utlbuffer.h" +#include "mathlib/swap.h" + +#define LZSS_LOOKSHIFT 4 +#define LZSS_LOOKAHEAD ( 1 << LZSS_LOOKSHIFT ) + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +//----------------------------------------------------------------------------- +// Returns true if buffer is compressed. +//----------------------------------------------------------------------------- +bool CLZSS::IsCompressed( unsigned char *pInput ) +{ + lzss_header_t *pHeader = (lzss_header_t *)pInput; + if ( pHeader && pHeader->id == LZSS_ID ) + { + return true; + } + + // unrecognized + return false; +} + +//----------------------------------------------------------------------------- +// Returns uncompressed size of compressed input buffer. Used for allocating output +// buffer for decompression. Returns 0 if input buffer is not compressed. +//----------------------------------------------------------------------------- +unsigned int CLZSS::GetActualSize( unsigned char *pInput ) +{ + lzss_header_t *pHeader = (lzss_header_t *)pInput; + if ( pHeader && pHeader->id == LZSS_ID ) + { + return LittleLong( pHeader->actualSize ); + } + + // unrecognized + return 0; +} + +void CLZSS::BuildHash( unsigned char *pData ) +{ + lzss_list_t *pList; + lzss_node_t *pTarget; + + int targetindex = (unsigned int)( uintp( pData ) & 0xFFFFFFFF ) & ( m_nWindowSize - 1 ); + pTarget = &m_pHashTarget[targetindex]; + if ( pTarget->pData ) + { + pList = &m_pHashTable[*pTarget->pData]; + if ( pTarget->pPrev ) + { + pList->pEnd = pTarget->pPrev; + pTarget->pPrev->pNext = 0; + } + else + { + pList->pEnd = 0; + pList->pStart = 0; + } + } + + pList = &m_pHashTable[*pData]; + pTarget->pData = pData; + pTarget->pPrev = 0; + pTarget->pNext = pList->pStart; + if ( pList->pStart ) + { + pList->pStart->pPrev = pTarget; + } + else + { + pList->pEnd = pTarget; + } + pList->pStart = pTarget; +} + +unsigned char *CLZSS::CompressNoAlloc( unsigned char *pInput, int inputLength, unsigned char *pOutputBuf, unsigned int *pOutputSize ) +{ + if ( inputLength <= sizeof( lzss_header_t ) + 8 ) + { + return NULL; + } + + // create the compression work buffers, small enough (~64K) for stack + m_pHashTable = (lzss_list_t *)stackalloc( 256 * sizeof( lzss_list_t ) ); + memset( m_pHashTable, 0, 256 * sizeof( lzss_list_t ) ); + m_pHashTarget = (lzss_node_t *)stackalloc( m_nWindowSize * sizeof( lzss_node_t ) ); + memset( m_pHashTarget, 0, m_nWindowSize * sizeof( lzss_node_t ) ); + + // allocate the output buffer, compressed buffer is expected to be less, caller will free + unsigned char *pStart = pOutputBuf; + // prevent compression failure (inflation), leave enough to allow dribble eof bytes + unsigned char *pEnd = pStart + inputLength - sizeof ( lzss_header_t ) - 8; + + // set the header + lzss_header_t *pHeader = (lzss_header_t *)pStart; + pHeader->id = LZSS_ID; + pHeader->actualSize = LittleLong( inputLength ); + + unsigned char *pOutput = pStart + sizeof (lzss_header_t); + unsigned char *pLookAhead = pInput; + unsigned char *pWindow = pInput; + unsigned char *pEncodedPosition = NULL; + unsigned char *pCmdByte = NULL; + int putCmdByte = 0; + + while ( inputLength > 0 ) + { + pWindow = pLookAhead - m_nWindowSize; + if ( pWindow < pInput ) + { + pWindow = pInput; + } + + if ( !putCmdByte ) + { + pCmdByte = pOutput++; + *pCmdByte = 0; + } + putCmdByte = ( putCmdByte + 1 ) & 0x07; + + int encodedLength = 0; + int lookAheadLength = inputLength < LZSS_LOOKAHEAD ? inputLength : LZSS_LOOKAHEAD; + + lzss_node_t *pHash = m_pHashTable[pLookAhead[0]].pStart; + while ( pHash ) + { + int matchLength = 0; + int length = lookAheadLength; + while ( length-- && pHash->pData[matchLength] == pLookAhead[matchLength] ) + { + matchLength++; + } + if ( matchLength > encodedLength ) + { + encodedLength = matchLength; + pEncodedPosition = pHash->pData; + } + if ( matchLength == lookAheadLength ) + { + break; + } + pHash = pHash->pNext; + } + + if ( encodedLength >= 3 ) + { + *pCmdByte = ( *pCmdByte >> 1 ) | 0x80; + *pOutput++ = ( ( pLookAhead-pEncodedPosition-1 ) >> LZSS_LOOKSHIFT ); + *pOutput++ = ( ( pLookAhead-pEncodedPosition-1 ) << LZSS_LOOKSHIFT ) | ( encodedLength-1 ); + } + else + { + encodedLength = 1; + *pCmdByte = ( *pCmdByte >> 1 ); + *pOutput++ = *pLookAhead; + } + + for ( int i=0; i= pEnd ) + { + // compression is worse, abandon + return NULL; + } + } + + if ( inputLength != 0 ) + { + // unexpected failure + Assert( 0 ); + return NULL; + } + + if ( !putCmdByte ) + { + pCmdByte = pOutput++; + *pCmdByte = 0x01; + } + else + { + *pCmdByte = ( ( *pCmdByte >> 1 ) | 0x80 ) >> ( 7 - putCmdByte ); + } + + *pOutput++ = 0; + *pOutput++ = 0; + + if ( pOutputSize ) + { + *pOutputSize = pOutput - pStart; + } + + return pStart; +} + +//----------------------------------------------------------------------------- +// Compress an input buffer. Caller must free output compressed buffer. +// Returns NULL if compression failed (i.e. compression yielded worse results) +//----------------------------------------------------------------------------- +unsigned char* CLZSS::Compress( unsigned char *pInput, int inputLength, unsigned int *pOutputSize ) +{ + unsigned char *pStart = (unsigned char *)malloc( inputLength ); + unsigned char *pFinal = CompressNoAlloc( pInput, inputLength, pStart, pOutputSize ); + if ( !pFinal ) + { + free( pStart ); + return NULL; + } + + return pStart; +} + +/* +// BUG BUG: This code is flaky, don't use until it's debugged!!! +unsigned int CLZSS::Uncompress( unsigned char *pInput, CUtlBuffer &buf ) +{ + int cmdByte = 0; + int getCmdByte = 0; + + unsigned int actualSize = GetActualSize( pInput ); + if ( !actualSize ) + { + // unrecognized + return 0; + } + + unsigned char *pBase = ( unsigned char * )buf.Base(); + + pInput += sizeof( lzss_header_t ); + + while ( !buf.IsValid() ) + { + if ( !getCmdByte ) + { + cmdByte = *pInput++; + } + getCmdByte = ( getCmdByte + 1 ) & 0x07; + + if ( cmdByte & 0x01 ) + { + int position = *pInput++ << LZSS_LOOKSHIFT; + position |= ( *pInput >> LZSS_LOOKSHIFT ); + int count = ( *pInput++ & 0x0F ) + 1; + if ( count == 1 ) + { + break; + } + unsigned int pos = buf.TellPut(); + unsigned char *pSource = ( pBase + pos ) - position - 1; + + // BUGBUG: + // This is failing!!! + // buf.WriteBytes( pSource, count ); + // So have to iterate them manually + for ( int i =0; i < count; ++i ) + { + buf.PutUnsignedChar( *pSource++ ); + } + } + else + { + buf.PutUnsignedChar( *pInput++ ); + } + cmdByte = cmdByte >> 1; + } + + if ( buf.TellPut() != (int)actualSize ) + { + // unexpected failure + Assert( 0 ); + return 0; + } + + return buf.TellPut(); +} +*/ + +unsigned int CLZSS::SafeUncompress( unsigned char *pInput, unsigned char *pOutput, unsigned int unBufSize ) +{ + unsigned int totalBytes = 0; + int cmdByte = 0; + int getCmdByte = 0; + + unsigned int actualSize = GetActualSize( pInput ); + if ( !actualSize ) + { + // unrecognized + return 0; + } + + if ( actualSize > unBufSize ) + { + return 0; + } + + pInput += sizeof( lzss_header_t ); + + for ( ;; ) + { + if ( !getCmdByte ) + { + cmdByte = *pInput++; + } + getCmdByte = ( getCmdByte + 1 ) & 0x07; + + if ( cmdByte & 0x01 ) + { + int position = *pInput++ << LZSS_LOOKSHIFT; + position |= ( *pInput >> LZSS_LOOKSHIFT ); + int count = ( *pInput++ & 0x0F ) + 1; + if ( count == 1 ) + { + break; + } + unsigned char *pSource = pOutput - position - 1; + + if ( totalBytes + count > unBufSize ) + { + return 0; + } + + for ( int i=0; i unBufSize ) + return 0; + + *pOutput++ = *pInput++; + totalBytes++; + } + cmdByte = cmdByte >> 1; + } + + if ( totalBytes != actualSize ) + { + // unexpected failure + Assert( 0 ); + return 0; + } + + return totalBytes; +} + +//----------------------------------------------------------------------------- +// Uncompress a buffer, Returns the uncompressed size. Caller must provide an +// adequate sized output buffer or memory corruption will occur. +//----------------------------------------------------------------------------- +unsigned int CLZSS::Uncompress( unsigned char *pInput, unsigned char *pOutput ) +{ + unsigned int totalBytes = 0; + int cmdByte = 0; + int getCmdByte = 0; + + unsigned int actualSize = GetActualSize( pInput ); + if ( !actualSize ) + { + // unrecognized + return 0; + } + + pInput += sizeof( lzss_header_t ); + + for ( ;; ) + { + if ( !getCmdByte ) + { + cmdByte = *pInput++; + } + getCmdByte = ( getCmdByte + 1 ) & 0x07; + + if ( cmdByte & 0x01 ) + { + int position = *pInput++ << LZSS_LOOKSHIFT; + position |= ( *pInput >> LZSS_LOOKSHIFT ); + int count = ( *pInput++ & 0x0F ) + 1; + if ( count == 1 ) + { + break; + } + unsigned char *pSource = pOutput - position - 1; + for ( int i=0; i> 1; + } + + if ( totalBytes != actualSize ) + { + // unexpected failure + Assert( 0 ); + return 0; + } + + return totalBytes; +} diff --git a/r5dev/vproj/clientsdk.vcxproj b/r5dev/vproj/clientsdk.vcxproj index cd54ee6e..3484031a 100644 --- a/r5dev/vproj/clientsdk.vcxproj +++ b/r5dev/vproj/clientsdk.vcxproj @@ -355,6 +355,8 @@ + + @@ -621,7 +623,7 @@ stdcpp17 Default /D "GAMESDK" /D "CLIENT_DLL" /D "_CRT_SECURE_NO_WARNINGS" /D "CURL_STATICLIB" /D "SPDLOG_COMPILED_LIB" %(AdditionalOptions) - $(ProjectDir)..\;$(ProjectDir)..\thirdparty\;$(ProjectDir)..\thirdparty\imgui\ + $(ProjectDir)..\;$(ProjectDir)..\public\;$(ProjectDir)..\thirdparty\;$(ProjectDir)..\thirdparty\imgui\ Precise true @@ -664,7 +666,7 @@ true /D "GAMESDK" /D "CLIENT_DLL" /D "_CRT_SECURE_NO_WARNINGS" /D "CURL_STATICLIB" /D "SPDLOG_COMPILED_LIB" /D "SPDLOG_NO_EXCEPTIONS" %(AdditionalOptions) - $(ProjectDir)..\;$(ProjectDir)..\thirdparty\;$(ProjectDir)..\thirdparty\imgui\ + $(ProjectDir)..\;$(ProjectDir)..\public\;$(ProjectDir)..\thirdparty\;$(ProjectDir)..\thirdparty\imgui\ Precise false true @@ -712,7 +714,7 @@ false /D "GAMESDK" /D "CLIENT_DLL" /D "_CRT_SECURE_NO_WARNINGS" /D "CURL_STATICLIB" /D "SPDLOG_COMPILED_LIB" %(AdditionalOptions) - $(ProjectDir)..\;$(ProjectDir)..\thirdparty\;$(ProjectDir)..\thirdparty\imgui\ + $(ProjectDir)..\;$(ProjectDir)..\public\;$(ProjectDir)..\thirdparty\;$(ProjectDir)..\thirdparty\imgui\ Full Precise true diff --git a/r5dev/vproj/clientsdk.vcxproj.filters b/r5dev/vproj/clientsdk.vcxproj.filters index 2c3d384f..8a19b2d2 100644 --- a/r5dev/vproj/clientsdk.vcxproj.filters +++ b/r5dev/vproj/clientsdk.vcxproj.filters @@ -223,6 +223,12 @@ {f43b13e8-00f9-4fd8-8624-8e6376ea354a} + + {c94410d7-b776-44d7-ad60-2afb250f60c3} + + + {f0fa0bdd-12a4-429d-910c-a0a45f71f454} + @@ -1691,6 +1697,12 @@ sdk\public + + sdk\public\tier0 + + + sdk\public\tier1 + diff --git a/r5dev/vproj/dedicated.vcxproj b/r5dev/vproj/dedicated.vcxproj index c77cfb04..00e7fbea 100644 --- a/r5dev/vproj/dedicated.vcxproj +++ b/r5dev/vproj/dedicated.vcxproj @@ -92,7 +92,7 @@ core\stdafx.h stdcpp17 Default - $(ProjectDir)..\;$(ProjectDir)..\thirdparty\;$(ProjectDir)..\thirdparty\recast\ + $(ProjectDir)..\;$(ProjectDir)..\public\;$(ProjectDir)..\thirdparty\;$(ProjectDir)..\thirdparty\recast\ Precise true @@ -127,7 +127,7 @@ Size Default false - $(ProjectDir)..\;$(ProjectDir)..\thirdparty\;$(ProjectDir)..\thirdparty\recast\ + $(ProjectDir)..\;$(ProjectDir)..\public\;$(ProjectDir)..\thirdparty\;$(ProjectDir)..\thirdparty\recast\ Precise false true @@ -167,7 +167,7 @@ Neither Default false - $(ProjectDir)..\;$(ProjectDir)..\thirdparty\;$(ProjectDir)..\thirdparty\recast\ + $(ProjectDir)..\;$(ProjectDir)..\public\;$(ProjectDir)..\thirdparty\;$(ProjectDir)..\thirdparty\recast\ Full false Precise @@ -350,6 +350,8 @@ + + diff --git a/r5dev/vproj/dedicated.vcxproj.filters b/r5dev/vproj/dedicated.vcxproj.filters index 442ce717..4058d7a4 100644 --- a/r5dev/vproj/dedicated.vcxproj.filters +++ b/r5dev/vproj/dedicated.vcxproj.filters @@ -172,6 +172,12 @@ {55c87120-310f-4eb5-81b9-3d8912b1dfb4} + + {503c54bf-8011-46a0-9439-a7ae035c23e0} + + + {36aae780-3dfa-4fb4-93d8-1e453c4be236} + @@ -1098,6 +1104,12 @@ sdk\public + + sdk\public\tier1 + + + sdk\public\tier0 + diff --git a/r5dev/vproj/gamesdk.vcxproj b/r5dev/vproj/gamesdk.vcxproj index 99181203..3ec59c4e 100644 --- a/r5dev/vproj/gamesdk.vcxproj +++ b/r5dev/vproj/gamesdk.vcxproj @@ -167,6 +167,7 @@ + @@ -409,6 +410,8 @@ + + @@ -675,7 +678,7 @@ stdcpp17 Default /D "_CRT_SECURE_NO_WARNINGS" /D "GAMESDK" /D "CURL_STATICLIB" /D "SPDLOG_COMPILED_LIB" %(AdditionalOptions) - $(ProjectDir)..\;$(ProjectDir)..\thirdparty\;$(ProjectDir)..\thirdparty\recast\;$(ProjectDir)..\thirdparty\imgui\ + $(ProjectDir)..\;$(ProjectDir)..\public\;$(ProjectDir)..\thirdparty\;$(ProjectDir)..\thirdparty\recast\;$(ProjectDir)..\thirdparty\imgui\ Precise true @@ -718,7 +721,7 @@ true /D "_CRT_SECURE_NO_WARNINGS" /D "GAMESDK" /D "CURL_STATICLIB" /D "SPDLOG_COMPILED_LIB" /D "SPDLOG_NO_EXCEPTIONS" %(AdditionalOptions) - $(ProjectDir)..\;$(ProjectDir)..\thirdparty\;$(ProjectDir)..\thirdparty\recast\;$(ProjectDir)..\thirdparty\imgui\ + $(ProjectDir)..\;$(ProjectDir)..\public\;$(ProjectDir)..\thirdparty\;$(ProjectDir)..\thirdparty\recast\;$(ProjectDir)..\thirdparty\imgui\ Precise false true @@ -766,7 +769,7 @@ false /D "_CRT_SECURE_NO_WARNINGS" /D "GAMESDK" /D "CURL_STATICLIB" /D "SPDLOG_COMPILED_LIB" %(AdditionalOptions) - $(ProjectDir)..\;$(ProjectDir)..\thirdparty\;$(ProjectDir)..\thirdparty\recast\;$(ProjectDir)..\thirdparty\imgui\ + $(ProjectDir)..\;$(ProjectDir)..\public\;$(ProjectDir)..\thirdparty\;$(ProjectDir)..\thirdparty\recast\;$(ProjectDir)..\thirdparty\imgui\ Full Precise true diff --git a/r5dev/vproj/gamesdk.vcxproj.filters b/r5dev/vproj/gamesdk.vcxproj.filters index 82d0abdd..c5b355f7 100644 --- a/r5dev/vproj/gamesdk.vcxproj.filters +++ b/r5dev/vproj/gamesdk.vcxproj.filters @@ -232,6 +232,12 @@ {0ceb19d4-45ba-4dd0-8ce1-ae87598c7a72} + + {9c046c8b-a67f-4bac-938e-5bc52bb1baf5} + + + {676c3f77-36b9-44e8-a0d2-ff5ed6a8040e} + @@ -732,6 +738,9 @@ thirdparty\imgui\misc + + sdk\tier1 + @@ -1862,6 +1871,12 @@ sdk\public + + sdk\public\tier0 + + + sdk\public\tier1 + diff --git a/r5dev/vproj/sdklauncher.vcxproj b/r5dev/vproj/sdklauncher.vcxproj index 23c45b86..e4ca34ad 100644 --- a/r5dev/vproj/sdklauncher.vcxproj +++ b/r5dev/vproj/sdklauncher.vcxproj @@ -98,7 +98,7 @@ stdcpp17 core\stdafx.h /D "_CRT_SECURE_NO_WARNINGS" /D "SDKLAUNCHER" /D "SPDLOG_COMPILED_LIB" %(AdditionalOptions) - $(ProjectDir)..\;$(ProjectDir)..\thirdparty\ + $(ProjectDir)..\;$(ProjectDir)..\public\;$(ProjectDir)..\thirdparty\; Precise true @@ -133,7 +133,7 @@ stdcpp17 core\stdafx.h /D "_CRT_SECURE_NO_WARNINGS" /D "SDKLAUNCHER" /D "SPDLOG_COMPILED_LIB" /D "SPDLOG_NO_EXCEPTIONS" %(AdditionalOptions) - $(ProjectDir)..\;$(ProjectDir)..\thirdparty\ + $(ProjectDir)..\;$(ProjectDir)..\public\;$(ProjectDir)..\thirdparty\; Precise false true @@ -173,7 +173,7 @@ stdcpp17 core\stdafx.h /D "_CRT_SECURE_NO_WARNINGS" /D "SDKLAUNCHER" /D "SPDLOG_COMPILED_LIB" %(AdditionalOptions) - $(ProjectDir)..\;$(ProjectDir)..\thirdparty\ + $(ProjectDir)..\;$(ProjectDir)..\public\;$(ProjectDir)..\thirdparty\; Full false Precise