diff --git a/appleauth/auth.cpp b/appleauth/auth.cpp index d15e7bc..e6d8f81 100644 --- a/appleauth/auth.cpp +++ b/appleauth/auth.cpp @@ -7,6 +7,23 @@ #include "tier0/rand.h" IHTTPClientManager *g_pHttpClientMgr = NULL; +static int base64_decode(const char *b64, unsigned char *pOut) { + int len = strlen(b64); + if (pOut == NULL) + return (len*3)/4; + int out_len = EVP_DecodeBlock(pOut, + (const unsigned char *)b64, + len); + if (out_len < 0) + return -1; + + while (len > 0 && b64[len - 1] == '=') { + out_len--; + len--; + } + + return out_len; +} // autogenerated #define APPLE_LOCAL_USER "e2e70285da39596ef06153b9c4e1e5dc8d2f983bc5cd63f5b1e292207060d931" @@ -43,6 +60,7 @@ public: virtual EAppleAuthStatus SubmitLoginData( const char *szEmail, const char *szPassword ) override; virtual EAppleAuthStatus Submit2FA( const char *szCode ) override; virtual CUtlString EncryptPassword( const char *szPassword, CUtlString szSalt, uint32_t uIters, EPasswordType eType ); + void ComputeM1( unsigned char M1[SHA256_DIGEST_LENGTH], const BIGNUM *N, const BIGNUM *g, const char *username, const char *password, const unsigned char *salt, size_t salt_len, const BIGNUM *A, const BIGNUM *B, const BIGNUM *a ); CUtlString FetchADIPB(); void FetchHeaders( CUtlString szAdiPb ); @@ -261,23 +279,20 @@ EAppleAuthStatus CAppleAuth::SubmitLoginData( const char *szEmail, const char *s BIGNUM* pA = BN_new(); BN_CTX *pbnCtx = BN_CTX_new(); - /* - CUtlBuffer bN; - CUtlBuffer bG; - */ SHA256_CTX pSha; - unsigned char szHash[SHA256_DIGEST_LENGTH]; + unsigned char pK[SHA256_DIGEST_LENGTH]; BN_hex2bn(&pN, N2048); BN_hex2bn(&pG, G2048); - /* + CUtlBuffer bN; + CUtlBuffer bG; bN = CUtlBuffer(BN_num_bytes(pN)); bG = CUtlBuffer(BN_num_bytes(pG)); BN_bn2bin(pN, bN.GetMemory()); SHA256_Init(&pSha); SHA256_Update(&pSha, bN.GetMemory(), bN.GetSize()); SHA256_Update(&pSha, bG.GetMemory(), bG.GetSize()); - SHA256_Final(szHash, &pSha); - */ + SHA256_Final(pK, &pSha); + BN_rand(pa, 256, 0, 0); BN_set_flags(pa, BN_FLG_CONSTTIME); BN_mod_exp(pA, pG, pa, pN, pbnCtx); @@ -368,39 +383,230 @@ EAppleAuthStatus CAppleAuth::SubmitLoginData( const char *szEmail, const char *s m_pGrandSlamClient->Post("/grandslam/GsService2", &header, plist.GetLenght(), plist); HTTPResponse_t stResponse = m_pGrandSlamClient->GetResponse(); V_printf("%i %i\n", stResponse.m_uCode, stResponse.m_bIsComplete); - if (stResponse.m_uCode == 200) + if (stResponse.m_uCode != 200) + return APPLE_AUTH_FAILURE; + V_printf("%s\n",stResponse.m_message.GetMemory()); + IJSONObject *pObject = PropertyListManager()->ReadString(stResponse.m_message); + IJSONObject *pResponse = pObject->GetValue("Response")->GetObject(); + CUtlString szEncryptedPassword = EncryptPassword(szPassword, pResponse->GetValue("s")->GetStringValue(), pResponse->GetValue("i")->GetNumberValue(), PASSWORD_TYPE_S2K); + const char *szSalt = pResponse->GetValue("s")->GetStringValue(); + const char *szB = pResponse->GetValue("B")->GetStringValue(); + unsigned char salt[32]; + int iSaltLen = base64_decode(szSalt, salt); + + int iBLen = base64_decode(szB, NULL); + CUtlBuffer BBuffer = CUtlBuffer(iBLen); + iBLen = base64_decode(szB, BBuffer.GetMemory()); + BIGNUM *pB = BN_bin2bn(BBuffer.GetMemory(), BBuffer.GetSize(), NULL); + unsigned char M1[SHA256_DIGEST_LENGTH]; + ComputeM1(M1, pN, pG, szEmail, szPassword, salt, iSaltLen, pA, pB, pa); + + CUtlString szM1 = {}; + for ( int i = 0; i < SHA256_DIGEST_LENGTH; i++ ) { - V_printf("%s\n",stResponse.m_message.GetMemory()); - IJSONObject *pObject = PropertyListManager()->ReadString(stResponse.m_message); - IJSONObject *pResponse = pObject->GetValue("Response")->GetObject(); - EncryptPassword(szPassword, pResponse->GetValue("s")->GetStringValue(), pResponse->GetValue("i")->GetNumberValue(), PASSWORD_TYPE_S2K); + szM1.AppendTail(CUtlString("%02x", M1[i])); } + + plist = CUtlString( + "\n" + "\n" + "\n" + "\n" + "\tHeader\n" + "\t\n" + "\tVersion\n" + "\t1.0.1\n" + "\t\n" + + "\tRequest\n" + "\t\n" + + "\tM1\n" + "\t%s\n" + + "\tc\n" + "\t%s\n" + + "\tps\n" + "\ts2ks2k_fo\n" + + "\tu\n" + "\t%s\n" + + "\to\n" + "\tcomplete\n" + + "\tcpd\n" + "\t\n" + + "\tX-Apple-I-Client-Time\n" + "\t2026-1-9T12:00:00Z\n" + "\tX-Apple-I-MD\n" + "\t%s\n" + "\tX-Apple-I-MD-LU\n" + "\t" APPLE_LOCAL_USER "\n" + "\tX-Apple-I-MD-M\n" + "\t%s\n" + "\tX-Apple-I-MD-RINFO\n" + "\t%s\n" + "\tX-Mme-Client-Info\n" + "\t<MacBookPro13,2> <macOS;13.1;22C65> <com.apple.AuthKit/1 (com.apple.dt.Xcode/3594.4.19)>\n" + "\tX-Apple-I-SRL-NO\n" + "\t0\n" + + + "\tbootstrap\n" + "\t\n" + + "\ticscrec\n" + "\t\n" + + + "\tpbe\n" + "\t\n" + + "\tprkgen\n" + "\t\n" + + "\tsvct\n" + "\tiCloud\n" + + "\tloc\n" + "\ten_US\n" + + "\t\n" + + "\t\n" + "\n" + "\n", szM1.GetString(), pResponse->GetValue("c")->GetStringValue(), szEmail, m_szAppleIMD.GetString(), m_szAppleIMDM.GetString(), m_szAppleIMDRINFO.GetString()); + V_printf("%s\n", plist.GetString()); + m_pGrandSlamClient->Post("/grandslam/GsService2", &header, plist.GetLenght(), plist); + stResponse = m_pGrandSlamClient->GetResponse(); + V_printf("%i %i\n", stResponse.m_uCode, stResponse.m_bIsComplete); + if (stResponse.m_uCode != 200) + return APPLE_AUTH_FAILURE; + V_printf("%s\n", stResponse.m_message.GetMemory()); + g_pHttpClientMgr->Disconnect(m_pGrandSlamClient); + OPENSSL_free(pszA); return APPLE_AUTH_SUCCESS; } +static void hash_bn_pad(unsigned char out[SHA256_DIGEST_LENGTH], const BIGNUM *bn) +{ + unsigned char buf[256]; + memset(buf, 0, sizeof(buf)); + + int len = BN_num_bytes(bn); + BN_bn2bin(bn, buf + (256 - len)); + + SHA256(buf, 256, out); +} + +void CAppleAuth::ComputeM1( + unsigned char M1[SHA256_DIGEST_LENGTH], + const BIGNUM *N, + const BIGNUM *g, + const char *username, + const char *password, + const unsigned char *salt, size_t salt_len, + const BIGNUM *A, + const BIGNUM *B, + const BIGNUM *a +) +{ + SHA256_CTX ctx; + unsigned char HN[SHA256_DIGEST_LENGTH], Hg[SHA256_DIGEST_LENGTH], HNxorg[SHA256_DIGEST_LENGTH]; + unsigned char HI[SHA256_DIGEST_LENGTH]; + + /* H(N) xor H(g) */ + hash_bn_pad(HN, N); + hash_bn_pad(Hg, g); + for (int i = 0; i < SHA256_DIGEST_LENGTH; i++) + HNxorg[i] = HN[i] ^ Hg[i]; + + /* H(I) */ + SHA256((const unsigned char *)username, strlen(username), HI); + + /* x = H(s || H(I ":" P)) */ + unsigned char inner_hash[SHA256_DIGEST_LENGTH]; + SHA256_Init(&ctx); + SHA256_Update(&ctx, (const unsigned char *)username, strlen(username)); + SHA256_Update(&ctx, (const unsigned char *)":", 1); + SHA256_Update(&ctx, (const unsigned char *)password, strlen(password)); + SHA256_Final(inner_hash, &ctx); + + unsigned char x_hash[SHA256_DIGEST_LENGTH]; + SHA256_Init(&ctx); + SHA256_Update(&ctx, salt, salt_len); + SHA256_Update(&ctx, inner_hash, SHA256_DIGEST_LENGTH); + SHA256_Final(x_hash, &ctx); + + BIGNUM *x = BN_bin2bn(x_hash, SHA256_DIGEST_LENGTH, NULL); + + /* k = H(N || g) */ + unsigned char k_hash[SHA256_DIGEST_LENGTH]; + SHA256_Init(&ctx); + unsigned char bufN[256], bufg[256]; + memset(bufN, 0, 256); BN_bn2bin(N, bufN + (256 - BN_num_bytes(N))); + memset(bufg, 0, 256); BN_bn2bin(g, bufg + (256 - BN_num_bytes(g))); + SHA256_Update(&ctx, bufN, 256); + SHA256_Update(&ctx, bufg, 256); + SHA256_Final(k_hash, &ctx); + BIGNUM *k = BN_bin2bn(k_hash, SHA256_DIGEST_LENGTH, NULL); + + /* u = H(A || B) */ + unsigned char bufA[256], bufB[256], u_hash[SHA256_DIGEST_LENGTH]; + memset(bufA, 0, 256); BN_bn2bin(A, bufA + (256 - BN_num_bytes(A))); + memset(bufB, 0, 256); BN_bn2bin(B, bufB + (256 - BN_num_bytes(B))); + SHA256_Init(&ctx); + SHA256_Update(&ctx, bufA, 256); + SHA256_Update(&ctx, bufB, 256); + SHA256_Final(u_hash, &ctx); + BIGNUM *u = BN_bin2bn(u_hash, SHA256_DIGEST_LENGTH, NULL); + + /* S = (B - k * g^x)^(a + u * x) mod N */ + BN_CTX *bn_ctx = BN_CTX_new(); + BIGNUM *gx = BN_new(), *kgx = BN_new(), *B_sub = BN_new(); + BIGNUM *ux = BN_new(), *exp = BN_new(), *S = BN_new(); + + BN_mod_exp(gx, g, x, N, bn_ctx); /* g^x mod N */ + BN_mod_mul(kgx, k, gx, N, bn_ctx); /* k * g^x mod N */ + BN_mod_sub(B_sub, B, kgx, N, bn_ctx); /* B - k*g^x mod N */ + BN_mul(ux, u, x, bn_ctx); /* u*x */ + BN_add(exp, a, ux); /* a + u*x */ + BN_mod_exp(S, B_sub, exp, N, bn_ctx); /* S */ + + /* K = H(S) */ + unsigned char S_bytes[256]; + memset(S_bytes, 0, 256); + BN_bn2bin(S, S_bytes + (256 - BN_num_bytes(S))); + unsigned char K[SHA256_DIGEST_LENGTH]; + SHA256(S_bytes, 256, K); + + /* Compute M1 */ + SHA256_Init(&ctx); + SHA256_Update(&ctx, HNxorg, SHA256_DIGEST_LENGTH); + SHA256_Update(&ctx, HI, SHA256_DIGEST_LENGTH); + SHA256_Update(&ctx, salt, salt_len); + SHA256_Update(&ctx, bufA, 256); + SHA256_Update(&ctx, bufB, 256); + SHA256_Update(&ctx, K, SHA256_DIGEST_LENGTH); + SHA256_Final(M1, &ctx); + + /* Cleanup */ + BN_free(x); BN_free(k); BN_free(u); + BN_free(gx); BN_free(kgx); BN_free(B_sub); BN_free(ux); BN_free(exp); BN_free(S); + BN_CTX_free(bn_ctx); +} + EAppleAuthStatus CAppleAuth::Submit2FA( const char *szCode ) { } - -int base64_decode(const char *b64, unsigned char *out) { - int len = strlen(b64); - int out_len = EVP_DecodeBlock(out, - (const unsigned char *)b64, - len); - if (out_len < 0) - return -1; - - while (len > 0 && b64[len - 1] == '=') { - out_len--; - len--; - } - - return out_len; -} CUtlString CAppleAuth::EncryptPassword( const char *szPassword, CUtlString szSalt, uint32_t uIters, EPasswordType eType ) { diff --git a/brv/brv.h b/brv/brv.h new file mode 100644 index 0000000..e69de29 diff --git a/brv/build.cpp b/brv/build.cpp new file mode 100644 index 0000000..e69de29 diff --git a/brv/cbrv.h b/brv/cbrv.h new file mode 100644 index 0000000..e69de29 diff --git a/brv/modern.cpp b/brv/modern.cpp new file mode 100644 index 0000000..e69de29 diff --git a/fpc/Makefile b/fpc/Makefile index 6b8a24b..be0d70b 100644 --- a/fpc/Makefile +++ b/fpc/Makefile @@ -1,7 +1,7 @@ # We want to build just enough to use other stuff TIER0_FILES := ../tier0/lib.cpp ../tier0/mem.cpp ../tier0/platform.cpp ../tier0/commandline.cpp ../tier0/rand.cpp TIER1_FILES := ../tier1/utlbuffer.cpp ../tier1/interface.cpp ../tier1/utlstring.cpp ../tier1/utlvector.cpp ../tier1/utlmap.cpp -TIER2_FILES := ../tier2/filesystem.cpp ../tier2/fileformats/ini.cpp +TIER2_FILES := ../tier2/filesystem.cpp ../tier2/fileformats/ini.cpp ../tier2/tokenizer.cpp ../tier2/fileformats/json.cpp FILESYSTEM_FILES := ../stdfilesystems/filesystem_libc.cpp TIER1_OBJS := $(TIER1_FILES:.cpp=.o) TIER2_OBJS := $(TIER2_FILES:.cpp=.o) @@ -57,8 +57,8 @@ build/libfilesystem_std.so: $(FILESYSTEM_FILES) build/libtier1.a build/libtier0. build/libfpcbuild.a: buildfile/interfaces.o builddir ar rcs build/libfpcbuild.a buildfile/interfaces.o -build/libfpc.so: $(FPC_FILES) builddir build/libfpcbuild.a build/libtier1.a - $(CC) $(CCFLAGS) $(FPC_FILES) library/libfpc.cpp -fPIC -shared -o build/libfpc.so build/libtier1.a +build/libfpc.so: $(FPC_FILES) builddir build/libfpcbuild.a build/libtier1.a build/libtier2.a + $(CC) $(CCFLAGS) $(FPC_FILES) library/libfpc.cpp -fPIC -shared -o build/libfpc.so build/libtier1.a build/libtier2.a builddir: mkdir -p build diff --git a/fpc/library/clang/c.cpp b/fpc/library/clang/c.cpp index 79ca81e..5ff0d62 100644 --- a/fpc/library/clang/c.cpp +++ b/fpc/library/clang/c.cpp @@ -9,6 +9,7 @@ #include "tier1/interface.h" #include "tier1/utlstring.h" #include "tier1/utlvector.h" +#include "tier2/fileformats/json.h" #include "libgen.h" #include "ctype.h" @@ -245,23 +246,33 @@ skipcompile: void CClangCompiler::GenerateLinterData() { + CUtlVector jsonValues = {}; FILE* f = V_fopen("compile_commands.json", "wb"); - V_fprintf(f, "[\n"); - uint32_t i = 0; - for (auto &file: g_clangFiles) + for ( auto &f: g_clangFiles ) { - V_fprintf(f, "\t{\n"); - V_fprintf(f, "\t\t\"arguments\": [\n"); - for (auto &arg: file.m_szArguments) - V_fprintf(f, "\t\t\t\"%s\",\n",arg.GetString()); - - V_fseek(f, -2, SEEK_CUR); - V_fprintf(f, "\n\t\t],\n"); - V_fprintf(f, "\t\t\"file\": \"%s\",\n", file.m_szName.GetString()); - V_fprintf(f, "\t\t\"directory\": \"%s\"\n", filesystem2->BuildDirectory()); - V_fprintf(f, "\t},\n"); + IJSONObject *pFileObject = JSONManager()->CreateObject(); + IJSONValue *pFileValue = JSONManager()->CreateValue(); + IJSONArray *pArgumentFiles = JSONManager()->CreateArray(); + IJSONValue *pArgumentsValue = JSONManager()->CreateValue(); + CUtlVector values; + + for (auto &arg: f.m_szArguments) + { + IJSONValue *pFileValue = JSONManager()->CreateValue(); + pFileValue->SetStringValue(arg.GetString()); + values.AppendTail(pFileValue); + } + pArgumentFiles->SetArray(values.GetSize(), values.GetData()); + pArgumentsValue->SetArrayValue(pArgumentFiles); + pFileObject->SetValue("arguments", pArgumentsValue); + pFileValue->SetObjectValue(pFileObject); + jsonValues.AppendTail(pFileValue); }; - V_fseek(f, -2, SEEK_CUR); - V_fprintf(f, "\n]\n"); + IJSONArray *pArray = JSONManager()->CreateArray(); + pArray->SetArray(jsonValues.GetSize(), jsonValues.GetData()); + IJSONValue *pRoot = JSONManager()->CreateValue(); + pRoot->SetArrayValue(pArray); + CUtlString szCommands = JSONManager()->WriteString(pRoot); + V_printf("JSON\n%s\n",szCommands.GetString()); V_fclose(f); }; diff --git a/fpc/main.cpp b/fpc/main.cpp index ecb30d7..7d9cb6c 100644 --- a/fpc/main.cpp +++ b/fpc/main.cpp @@ -3,6 +3,7 @@ #include "tier0/commandline.h" #include "tier1/interface.h" #include "tier1/utlvector.h" +#include "tier1/utlstring.h" #include "tier2/ifilesystem.h" #include "tier2/fileformats/ini.h" #include "public/c.h" diff --git a/fpc/sysroots/ios.cpp b/fpc/sysroots/ios.cpp new file mode 100644 index 0000000..e69de29 diff --git a/fpc/sysroots/macos.cpp b/fpc/sysroots/macos.cpp new file mode 100644 index 0000000..e69de29 diff --git a/fpc/sysroots/wdk.cpp b/fpc/sysroots/wdk.cpp new file mode 100644 index 0000000..e69de29 diff --git a/fpc/sysroots/xcode.cpp b/fpc/sysroots/xcode.cpp new file mode 100644 index 0000000..58c428e --- /dev/null +++ b/fpc/sysroots/xcode.cpp @@ -0,0 +1,10 @@ +#include "c.h" +#include "ld.h" +#include "helper.h" +#include "sysroots.h" + +DECLARE_SYSROOT_INSTALL_STAGE(xcode_install) +{ + + return 0; +} diff --git a/public/tier2/fileformats/json.h b/public/tier2/fileformats/json.h index ec2f188..d27a629 100644 --- a/public/tier2/fileformats/json.h +++ b/public/tier2/fileformats/json.h @@ -2,6 +2,7 @@ #define TIER2_JSON_H #include "tier0/platform.h" +#include "tier1/utlstring.h" class IJSONObject; class IJSONArray; @@ -27,9 +28,9 @@ abstract_class IJSONArray { public: virtual uint32_t GetCount() = 0; - virtual IJSONObject *GetParameter( uint32_t i ) = 0; + virtual IJSONValue *GetParameter( uint32_t i ) = 0; - virtual void SetArray( uint32_t uCount, IJSONValue *pValue ) = 0; + virtual void SetArray( uint32_t uCount, IJSONValue **ppValues ) = 0; virtual void CopyTo( IJSONArray *pObject ) = 0; virtual void Free() = 0; @@ -77,7 +78,8 @@ public: virtual IJSONValue *CreateValue( ) = 0; virtual void FreeValue( IJSONValue *pValue ) = 0; - virtual IJSONObject *ReadString( const char *szString ) = 0; + virtual IJSONValue *ReadString( const char *szString ) = 0; + virtual CUtlString WriteString( IJSONValue *pValue ) = 0; }; IJSONManager *JSONManager(); diff --git a/tier2/fileformats/json.cpp b/tier2/fileformats/json.cpp index 18e832b..0502250 100644 --- a/tier2/fileformats/json.cpp +++ b/tier2/fileformats/json.cpp @@ -5,28 +5,32 @@ abstract_class CJSONArray: public IJSONArray { public: virtual uint32_t GetCount() override; - virtual IJSONObject *GetParameter( uint32_t i ) override; + virtual IJSONValue *GetParameter( uint32_t i ) override; - virtual void SetArray( uint32_t uCount, IJSONValue *pValue ) override; + virtual void SetArray( uint32_t uCount, IJSONValue **ppValues ) override; virtual void CopyTo( IJSONArray *pObject ) override; virtual void Free() override; + + CUtlVector m_values; }; uint32_t CJSONArray::GetCount() { - + return m_values.GetSize(); } -IJSONObject *CJSONArray::GetParameter( uint32_t i ) +IJSONValue *CJSONArray::GetParameter( uint32_t i ) { - + return m_values[i]; } -void CJSONArray::SetArray( uint32_t uCount, IJSONValue *pValue ) +void CJSONArray::SetArray( uint32_t uCount, IJSONValue **ppValues ) { - + m_values.Resize(uCount); + for ( uint32_t u = 0; u < uCount; u++) + m_values[u] = ppValues[u]; } void CJSONArray::CopyTo( IJSONArray *pObject ) @@ -36,7 +40,9 @@ void CJSONArray::CopyTo( IJSONArray *pObject ) void CJSONArray::Free() { - + for ( auto &value: m_values) + JSONManager()->FreeValue(value); + m_values = {}; } abstract_class CJSONValue: public IJSONValue @@ -118,30 +124,34 @@ void CJSONValue::MakeNULL() void CJSONValue::SetStringValue( const char *szString ) { MakeNULL(); + m_eType = JSON_PARAMETER_STRING; m_szString = szString; } void CJSONValue::SetNumberValue( float fValue ) { MakeNULL(); + m_eType = JSON_PARAMETER_NUMBER; m_fValue = fValue; } void CJSONValue::SetBooleanValue( bool bValue ) { MakeNULL(); - + m_eType = JSON_PARAMETER_BOOLEAN; } void CJSONValue::SetArrayValue( IJSONArray *pValue ) { MakeNULL(); - + m_eType = JSON_PARAMETER_ARRAY; + m_pArray = pValue; } void CJSONValue::SetObjectValue( IJSONObject *pValue ) { MakeNULL(); + m_eType = JSON_PARAMETER_OBJECT; m_pObject = pValue; } @@ -234,13 +244,17 @@ public: virtual IJSONValue *CreateValue( ) override; virtual void FreeValue( IJSONValue *pValue ) override; + virtual IJSONValue *ReadString( const char *szString ) override; + virtual CUtlString WriteString( IJSONValue *pValue ) override; + private: + CUtlString RealWriteString( IJSONValue *pValue, uint32_t uOffest ); + static bool ExpectedToken( Token_t &token, const char *szValue ); static CUtlString GetQuotedToken( Token_t &token ); IJSONObject *ParseObject( Token_t *&pToken, const Token_t *pEnding ); IJSONArray *ParseArray( Token_t *&pToken, const Token_t *pEnding ); IJSONValue *ParseValue( Token_t *&pToken, const Token_t *pEnding ); - virtual IJSONObject *ReadString( const char *szString ) override; }; IJSONObject *CJSONManager::CreateObject( ) @@ -256,12 +270,13 @@ void CJSONManager::FreeObject( IJSONObject *pObject ) IJSONArray *CJSONManager::CreateArray( ) { - + return new CJSONArray; } void CJSONManager::FreeArray( IJSONArray *pArray ) { - + pArray->Free(); + delete (CJSONObject*)pArray; } IJSONValue *CJSONManager::CreateValue( ) @@ -343,7 +358,7 @@ not_comma: V_printf("%i: comma (,) or } was expected but got %s\n", pToken->m_iLine, pToken->m_szValue.GetString()); return NULL; not_colon: - V_printf("%i: colon (:)was expected but got %s\n", pToken->m_iLine, pToken->m_szValue.GetString()); + V_printf("%i: colon (:) was expected but got %s\n", pToken->m_iLine, pToken->m_szValue.GetString()); return NULL; not_quoted: @@ -357,22 +372,66 @@ eof: IJSONArray *CJSONManager::ParseArray( Token_t *&pToken, const Token_t *pEnding ) { + IJSONArray *pObject; + CUtlString szParamName; + IJSONValue *pValue; + CUtlVector values; + + if ( !ExpectedToken(*(pToken), "[") ) + return NULL; + NEXT_TOKEN(); + pObject = CreateArray(); + + // object might be empty + if ( ExpectedToken(*pToken, "]") ) + { + NEXT_TOKEN(); + return pObject; + } + + while(true) + { + pValue = ParseValue(pToken, pEnding); + values.AppendTail(pValue); + + if ( !ExpectedToken(*pToken, ",") ) + { + if ( !ExpectedToken(*pToken, "]") ) + { + goto not_comma; + } + + pObject->SetArray(values.GetSize(), values.GetData()); + return pObject; + } + NEXT_TOKEN(); + } + return pObject; +not_comma: + V_printf("%i: comma (,) or } was expected but got %s\n", pToken->m_iLine, pToken->m_szValue.GetString()); return NULL; + eof: V_printf("EOF\n"); - return NULL; + pObject->SetArray(values.GetSize(), values.GetData()); + return pObject; } IJSONValue *CJSONManager::ParseValue( Token_t *&pToken, const Token_t *pEnding ) { IJSONObject *pObject = ParseObject(pToken, pEnding); - IJSONArray *pArray = NULL; + IJSONArray *pArray = ParseArray(pToken, pEnding); IJSONValue *pValue = CreateValue(); if (pObject) { pValue->SetObjectValue(pObject); return pValue; } + if (pArray) + { + pValue->SetArrayValue(pArray); + return pValue; + } if ( GetQuotedToken(*pToken) != NULL ) { pValue->SetStringValue(pToken->m_szValue); @@ -386,19 +445,81 @@ eof: } -IJSONObject *CJSONManager::ReadString( const char *psz ) +IJSONValue *CJSONManager::ReadString( const char *psz ) { CUtlVector tokens; CUtlVector stack; - IJSONObject *pGlobalObject = NULL; + IJSONValue *pGlobalObject = NULL; tokens = Tokenize(psz); Token_t *pCurrentToken = tokens.GetData(); - pGlobalObject = ParseObject(pCurrentToken, tokens.end().m_pCurrent); + pGlobalObject = ParseValue(pCurrentToken, tokens.end().m_pCurrent); return pGlobalObject; }; +CUtlString CJSONManager::WriteString( IJSONValue *pValue ) +{ + return RealWriteString(pValue, 0); +} + +CUtlString CJSONManager::RealWriteString( IJSONValue *pValue, uint32_t uOffset ) +{ + IJSONArray *pArray; + CJSONObject *pObject; + CUtlString szString = ""; + if (pValue) + switch (pValue->GetType()) + { + case JSON_PARAMETER_NULL: + return CUtlString("null", pValue->GetNumberValue()); + case JSON_PARAMETER_BOOLEAN: + if (pValue->GetBooleanValue()) + return "true"; + return "false"; + case JSON_PARAMETER_NUMBER: + return CUtlString("\"%f\"", pValue->GetNumberValue()); + case JSON_PARAMETER_STRING: + return CUtlString("\"%s\"", pValue->GetStringValue()); + case JSON_PARAMETER_ARRAY: + pArray = pValue->GetArray(); + szString.AppendTail("[\n"); + for ( uint32_t i = 0; i < pArray->GetCount(); i++ ) + { + for ( uint32_t j = 0; j <= uOffset; j++) + szString.AppendTail("\t"); + szString.AppendTail(RealWriteString( pArray->GetParameter(i), uOffset+1 )); + if ( i != pArray->GetCount() - 1 ) + szString.AppendTail(","); + szString.AppendTail("\n"); + } + for ( uint32_t j = 0; j < uOffset; j++) + szString.AppendTail("\t"); + szString.AppendTail("]"); + return szString; + case JSON_PARAMETER_OBJECT: + pObject = (CJSONObject*)pValue->GetObject(); + szString.AppendTail("{\n"); + for ( uint32_t i = 0; i < pObject->m_params.GetSize(); i++ ) + { + for ( uint32_t j = 0; j <= uOffset; j++) + szString.AppendTail("\t"); + + szString.AppendTail(pObject->m_params[i].m_szName); + szString.AppendTail(": "); + + szString.AppendTail(RealWriteString( pObject->m_params[i].m_pValue, uOffset+1 )); + if ( i != pObject->m_params.GetSize() - 1 ) + szString.AppendTail(","); + szString.AppendTail("\n"); + } + for ( uint32_t j = 0; j < uOffset; j++) + szString.AppendTail("\t"); + szString.AppendTail("}"); + return szString; + } +} + IJSONManager *JSONManager() { static CJSONManager mgr;