Advertisement
Guest User

Untitled

a guest
Sep 8th, 2016
3,300
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 17.52 KB | None | 0 0
  1. #include "stdafx.h"
  2.  
  3. CSteamAPILoader g_steamAPILoader;
  4.  
  5.  
  6.  
  7.  
  8. class AArcadeHTTP
  9. {
  10. public:
  11. void CheckLiveStreams();
  12. void OnLiveStreamsChecked(HTTPRequestCompleted_t *pResult, bool bIOFailure);
  13. CCallResult<AArcadeHTTP, HTTPRequestCompleted_t> m_callResultCheckLiveStreams;
  14. ISteamHTTP001* SteamHTTP;
  15.  
  16. };
  17.  
  18. void AArcadeHTTP::CheckLiveStreams()
  19. {
  20. HTTPRequestHandle pRequest = SteamHTTP->CreateHTTPRequest(k_EHTTPMethodGET, "http://anarcast.tv/testlive.php");
  21.  
  22. SteamAPICall_t hSteamAPICall;
  23. SteamHTTP->SendHTTPRequest(pRequest, &hSteamAPICall);
  24.  
  25. m_callResultCheckLiveStreams.Set(hSteamAPICall, this, &AArcadeHTTP::OnLiveStreamsChecked);
  26. }
  27.  
  28. void AArcadeHTTP::OnLiveStreamsChecked(HTTPRequestCompleted_t *pResult, bool bIOFailure)
  29. {
  30.  
  31.  
  32. if (pResult->m_bRequestSuccessful)
  33. {
  34. printf("Request successful!\n");
  35.  
  36. if (pResult->m_eStatusCode == k_EHTTPStatusCode200OK)
  37. {
  38. printf("Good status code!\n");
  39.  
  40. uint32 realSize;
  41. SteamHTTP->GetHTTPResponseBodySize(pResult->m_hRequest, &realSize);
  42. printf("Buffer size is: %i\n", realSize);
  43. uint8* pData = new uint8[realSize];
  44. SteamHTTP->GetHTTPResponseBodyData(pResult->m_hRequest, pData, realSize);
  45.  
  46. printf("It is: %s\n", (char*)pData);
  47. }
  48. }
  49. }
  50.  
  51. void CheckForCallbacks()
  52. {
  53. SteamAPI_RunCallbacks();
  54. }
  55.  
  56.  
  57. void CheckLiveStreams(ISteamHTTP001* steamHTTP)
  58. {
  59. AArcadeHTTP* pAArcadeHTTP = new AArcadeHTTP();
  60. pAArcadeHTTP->CheckLiveStreams();
  61. pAArcadeHTTP->SteamHTTP = steamHTTP;
  62. }
  63.  
  64.  
  65.  
  66. int main( int argc, char* argv[] )
  67. //int CALLBACK WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow )
  68. {
  69.  
  70.  
  71. HANDLE hConsole = GetStdHandle( STD_OUTPUT_HANDLE );
  72.  
  73. SetConsoleTextAttribute( hConsole, FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE );
  74.  
  75. char steamAccountName[33];
  76. char steamAccountPassword[33];
  77. AppId_t appID;
  78. SteamItemDef_t dropListDefinition;
  79.  
  80. if ( __argc == 5 ) {
  81. strcpy_s( steamAccountName, __argv[1] );
  82. strcpy_s( steamAccountPassword, __argv[2] );
  83. sscanf( __argv[3], "%d", &appID );
  84. sscanf( __argv[4], "%d", &dropListDefinition );
  85.  
  86. memset( __argv[1], 0, strlen( __argv[1] ) );
  87. memset( __argv[2], 0, strlen( __argv[2] ) );
  88.  
  89. printf( "Enter your Steam account name: %s\n", steamAccountName );
  90. printf( "Enter your Steam account password: \n" );
  91. printf( "Enter the AppID: %d\n", appID );
  92. printf( "Enter the drop list definition: %d\n", dropListDefinition );
  93. }
  94. else {
  95. //goto funcEnd;
  96. printf( "Enter your Steam account name: " );
  97. scanf( "%32s", steamAccountName );
  98. getchar(); // skip newline
  99.  
  100. printf( "Enter your Steam account password: " );
  101. scanf( "%32s", steamAccountPassword );
  102. getchar();
  103.  
  104. printf( "Enter the AppID: " );
  105. scanf( "%d", &appID );
  106. getchar();
  107.  
  108. printf( "Enter the drop list definition: " );
  109. scanf( "%d", &dropListDefinition );
  110. getchar();
  111. }
  112.  
  113. char consoleTitle[256];
  114. sprintf_s( consoleTitle, "Steam Item Drop Idler (%s)", steamAccountName );
  115. SetConsoleTitleA( consoleTitle );
  116.  
  117. // load steam stuff
  118. CreateInterfaceFn steam3Factory = g_steamAPILoader.GetSteam3Factory();
  119. if ( !steam3Factory ) {
  120. printf( "GetSteam3Factory failed\n" );
  121. goto funcEnd;
  122. }
  123.  
  124. IClientEngine* clientEngine = (IClientEngine*)steam3Factory( CLIENTENGINE_INTERFACE_VERSION, NULL );
  125. if ( !clientEngine ) {
  126. printf( "clientEngine is null\n" );
  127. goto funcEnd;
  128. }
  129.  
  130. ISteamClient017* steamClient = (ISteamClient017*)steam3Factory( STEAMCLIENT_INTERFACE_VERSION_017, NULL );
  131. if ( !steamClient ) {
  132. printf( "steamClient is null\n" );
  133. goto funcEnd;
  134. }
  135.  
  136. HSteamPipe hSteamPipe;
  137. HSteamUser hSteamUser = clientEngine->CreateLocalUser( &hSteamPipe, k_EAccountTypeIndividual );
  138. if ( !hSteamPipe || !hSteamUser ) {
  139. printf( "CreateLocalUser failed (1)\n" );
  140. goto funcEnd;
  141. }
  142.  
  143. IClientBilling* clientBilling = clientEngine->GetIClientBilling( hSteamUser, hSteamPipe, CLIENTBILLING_INTERFACE_VERSION );
  144. if ( !clientBilling ) {
  145. printf( "clientBilling is null\n" );
  146. goto funcEnd;
  147. }
  148.  
  149. IClientFriends* clientFriends = clientEngine->GetIClientFriends( hSteamUser, hSteamPipe, CLIENTFRIENDS_INTERFACE_VERSION );
  150. if ( !clientFriends ) {
  151. printf( "clientFriends is null\n" );
  152. goto funcEnd;
  153. }
  154.  
  155. IClientUser* clientUser = clientEngine->GetIClientUser( hSteamUser, hSteamPipe, CLIENTUSER_INTERFACE_VERSION );
  156. if ( !clientUser ) {
  157. printf( "clientUser is null\n" );
  158. goto funcEnd;
  159. }
  160.  
  161. IClientUtils* clientUtils = clientEngine->GetIClientUtils( hSteamPipe, CLIENTUTILS_INTERFACE_VERSION );
  162. if ( !clientUtils ) {
  163. printf( "clientUtils is null\n" );
  164. goto funcEnd;
  165. }
  166.  
  167. ISteamGameCoordinator001* steamGameCoordinator = (ISteamGameCoordinator001*)steamClient->GetISteamGenericInterface( hSteamUser, hSteamPipe, STEAMGAMECOORDINATOR_INTERFACE_VERSION_001 );
  168. if ( !steamGameCoordinator ) {
  169. printf( "steamGameCoordinator is null\n" );
  170. goto funcEnd;
  171. }
  172.  
  173. ISteamInventory001* steamInventory = (ISteamInventory001*)steamClient->GetISteamInventory( hSteamUser, hSteamPipe, "STEAMINVENTORY_INTERFACE_V001" );
  174. if ( !steamInventory ) {
  175. printf( "steamInventory is null\n" );
  176. goto funcEnd;
  177. }
  178.  
  179. ISteamUser017* steamUser = (ISteamUser017*)steamClient->GetISteamUser( hSteamUser, hSteamPipe, STEAMUSER_INTERFACE_VERSION_017 );
  180. if ( !steamUser ) {
  181. printf( "steamUser is null\n" );
  182. goto funcEnd;
  183. }
  184.  
  185. ISteamHTTP001* steamHTTP = (ISteamHTTP001*)steamClient->GetISteamHTTP(hSteamUser, hSteamPipe, STEAMHTTP_INTERFACE_VERSION_001);
  186. if (!steamHTTP) {
  187. printf("steamHTTP is null\n");
  188. goto funcEnd;
  189. }
  190.  
  191.  
  192.  
  193.  
  194. clientUser->LogOnWithPassword( false, steamAccountName, steamAccountPassword );
  195.  
  196. bool bPlayingGame = false;
  197. bool bPlayingOnServer = false; // for games that require us to be connected to a server
  198. while ( true )
  199. {
  200. // process steam user callbacks
  201. CallbackMsg_t callbackMsg;
  202. while ( Steam_BGetCallback( hSteamPipe, &callbackMsg ) )
  203. {
  204. switch ( callbackMsg.m_iCallback )
  205. {
  206. case SteamServersConnected_t::k_iCallback:
  207. clientFriends->SetPersonaState( k_EPersonaStateOnline );
  208.  
  209. if ( (*(bool( __thiscall** )(IClientUser*, AppId_t))(*(DWORD*)clientUser + 692))(clientUser, appID) ) { // BIsSubscribedApp
  210. clientUtils->SetAppIDForCurrentPipe( appID, true );
  211. bPlayingGame = true;
  212. }
  213. else {
  214. printf( "You are not subscribed to this app. Trying to add a free license...\n" );
  215.  
  216. SteamAPICall_t hRequestFreeLicenseForApps = (*(SteamAPICall_t( __thiscall** )(IClientBilling*, AppId_t*, int))(*(DWORD*)clientBilling + 24))(clientBilling, &appID, 1); // RequestFreeLicenseForApps
  217. bool bFailed;
  218. while ( !clientUtils->IsAPICallCompleted( hRequestFreeLicenseForApps, &bFailed ) )
  219. Sleep( 1000 );
  220.  
  221. RequestFreeLicenseResponse_t requestFreeLicenseResponse;
  222. if ( !clientUtils->GetAPICallResult( hRequestFreeLicenseForApps, &requestFreeLicenseResponse, sizeof( RequestFreeLicenseResponse_t ), RequestFreeLicenseResponse_t::k_iCallback, &bFailed ) ) {
  223. printf( "GetAPICallResult failed\n" );
  224. goto funcEnd;
  225. }
  226. if ( requestFreeLicenseResponse.m_EResult == k_EResultOK && requestFreeLicenseResponse.m_nGrantedAppIds == 1 ) {
  227. printf( "Added a free license\n" );
  228. clientUtils->SetAppIDForCurrentPipe( appID, true );
  229. bPlayingGame = true;
  230. }
  231. else {
  232. printf( "Failed to add a free license. You do not own this game\n" );
  233. goto funcEnd;
  234. }
  235. }
  236.  
  237. SetConsoleTextAttribute( hConsole, FOREGROUND_GREEN );
  238. printf( "Item drop idling is now in progress\n" );
  239. SetConsoleTextAttribute( hConsole, FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE );
  240. break;
  241. case SteamServerConnectFailure_t::k_iCallback:
  242. {
  243. SteamServerConnectFailure_t* steamServerConnectFailure = (SteamServerConnectFailure_t*)callbackMsg.m_pubParam;
  244. switch ( steamServerConnectFailure->m_eResult )
  245. {
  246. case k_EResultInvalidLoginAuthCode:
  247. printf( "Invalid Steam Guard code\n" );
  248. case k_EResultAccountLogonDenied:
  249. {
  250. char steamGuardCode[33];
  251. printf( "Enter the Steam Guard code: " );
  252. scanf( "%32s", steamGuardCode );
  253. getchar();
  254.  
  255. // this is Set2ndFactorAuthCode, however I have to do this because IClientUser.h is outdated
  256. (*(void( __thiscall** )(IClientUser*, const char*, bool))(*(DWORD*)clientUser + 676))(clientUser, steamGuardCode, false);
  257. clientUser->LogOnWithPassword( false, steamAccountName, steamAccountPassword );
  258. break;
  259. }
  260. case k_EResultTwoFactorCodeMismatch:
  261. printf( "Invalid Steam Mobile Authenticator code\n" );
  262. case k_EResultAccountLogonDeniedNeedTwoFactorCode:
  263. {
  264. char steamMobileAuthenticatorCode[33];
  265. printf( "Enter the Steam Mobile Authenticator code: " );
  266. scanf( "%32s", steamMobileAuthenticatorCode );
  267. getchar();
  268.  
  269. (*(void( __thiscall** )(IClientUser*, const char*))(*(DWORD*)clientUser + 196))(clientUser, steamMobileAuthenticatorCode); // SetTwoFactorCode
  270. clientUser->LogOnWithPassword( false, steamAccountName, steamAccountPassword );
  271. break;
  272. }
  273. default:
  274. SetConsoleTextAttribute( hConsole, FOREGROUND_RED );
  275. printf( "Login failed (%d)\n", steamServerConnectFailure->m_eResult );
  276. SetConsoleTextAttribute( hConsole, FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE );
  277. break;
  278. }
  279.  
  280. bPlayingGame = false;
  281. bPlayingOnServer = false;
  282. break;
  283. }
  284. case SteamServersDisconnected_t::k_iCallback:
  285. {
  286. SteamServersDisconnected_t* steamServersDisconnected = (SteamServersDisconnected_t*)callbackMsg.m_pubParam;
  287. printf( "Disconnected from steam servers (%d)\n", steamServersDisconnected->m_eResult );
  288.  
  289. bPlayingGame = false;
  290. bPlayingOnServer = false;
  291. break;
  292. }
  293. /*default:
  294. printf( "User callback: %d\n", callbackMsg.m_iCallback );
  295. break;*/
  296. }
  297.  
  298. Steam_FreeLastCallback( hSteamPipe );
  299. }
  300.  
  301. // do the actual item drop idling if we're "playing" the game
  302. if ( bPlayingGame ) {
  303. if ( appID == 440 ) {
  304. static bool bHelloMsgSent = false;
  305. static bool bGameServerInited = false;
  306.  
  307. // do game coordinator stuff
  308. if ( !bHelloMsgSent ) {
  309. // k_EMsgGCClientHello
  310. unsigned char response[] = { 0xA6, 0x0F, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x08, 0x98, 0xE1, 0xC0, 0x01 };
  311. steamGameCoordinator->SendMessage( 0x80000FA6, response, sizeof( response ) );
  312. printf( "Sent hello msg to game coordinator\n" );
  313.  
  314. bHelloMsgSent = true;
  315.  
  316. }
  317.  
  318. uint32 msgSize;
  319. while ( steamGameCoordinator->IsMessageAvailable( &msgSize ) ) {
  320. uint32 msgType;
  321. unsigned char* msg = new unsigned char[msgSize];
  322. if ( steamGameCoordinator->RetrieveMessage( &msgType, msg, msgSize, &msgSize ) == k_EGCResultOK )
  323. {
  324. printf( "Retrieved message of type 0x%X from game coordinator\n", msgType );
  325. char * s = new char[11];
  326. sprintf(s, "0x%X", msgType);
  327.  
  328.  
  329. printf(s);
  330. printf("\n");
  331.  
  332.  
  333.  
  334.  
  335.  
  336. if (msgType == 0x80000015) // new item
  337. {
  338. printf("msg 15\n");
  339.  
  340.  
  341. //sort
  342. //unsigned char* buff = new unsigned char[msgSize];
  343. unsigned char buff[] = { 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
  344. steamGameCoordinator->SendMessage(0x80000411, buff, sizeof(buff)); // 1041 sort
  345. printf("Sent sort msg to game coordinator\n");
  346.  
  347.  
  348. }
  349.  
  350. if (msgType == 0x80000422) // k_EMsgGCBackpackSortFinished on sort compleatedd
  351. {
  352. printf("msg sort compleated 0x80000422\n");
  353. }
  354. if (msgType == 0x422) // k_EMsgGCBackpackSortFinished on sort compleatedd
  355. {
  356.  
  357. //35CB7215600452368EB6A1366A349904
  358. printf("msg sort compleated 0x422\n");
  359.  
  360.  
  361. /*
  362.  
  363. HTTPRequestHandle req = steamHTTP->CreateHTTPRequest(k_EHTTPMethodGET, "http://tee.16mb.com");
  364. steamHTTP->SetHTTPRequestNetworkActivityTimeout(req, 60000); // 60s
  365. SteamAPICall_t hSteamAPICall;
  366.  
  367.  
  368.  
  369.  
  370. //SteamCallHandle()
  371.  
  372. if (steamHTTP->SendHTTPRequest(req, &hSteamAPICall))
  373. {
  374.  
  375.  
  376. //if (bRequestSuccessful && !bFailure && eStatusCode == k_EHTTPStatusCode200OK)
  377. //PrintToServer("CNetwork -> Request Was a Success!");
  378. uint32 *bodySize;
  379. // steamHTTP->GetHTTPResponseBodySize(req, bodySize);
  380. // int i = (int)bodySize;
  381. //char body[i];
  382. //steamHTTP->GetHTTPResponseBodyData(req, body, i);
  383.  
  384.  
  385.  
  386.  
  387. }*/
  388.  
  389. }
  390.  
  391.  
  392.  
  393.  
  394.  
  395.  
  396. if ( msgType == 0x80000FA4 ) { // k_EMsgGCClientWelcome
  397. printf( "Got welcome msg from game coordinator\n" );
  398. }
  399. else if (msgType == 0x8000001B) { // k_ESOMsg_CacheSubscriptionCheck
  400. // k_ESOMsg_CacheSubscriptionRefresh
  401. unsigned char response[] = { 0x1C, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
  402. *(CSteamID*)&response[9] = steamUser->GetSteamID();
  403. steamGameCoordinator->SendMessage(0x8000001C, response, sizeof(response));
  404. printf("Sent response to game coordinator\n");
  405.  
  406.  
  407.  
  408.  
  409.  
  410. //unsigned char* buff = new unsigned char[msgSize];
  411. unsigned char buff[] = { 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
  412. steamGameCoordinator->SendMessage(0x80000411, buff, sizeof(buff)); // 1041 sort
  413. printf("Sent sort msg to game coordinator\n");
  414.  
  415.  
  416.  
  417.  
  418.  
  419.  
  420.  
  421. }
  422.  
  423.  
  424.  
  425.  
  426.  
  427.  
  428.  
  429.  
  430.  
  431. }
  432. else {
  433. printf( "Failed to retrieve message from game coordinator\n" );
  434. }
  435. delete[] msg;
  436. }
  437.  
  438. // do game server stuff
  439. static HSteamPipe hSteamGameServerPipe;
  440. static HSteamUser hSteamGameServerUser;
  441. static ISteamGameServer012* steamGameServer;
  442. if ( !bGameServerInited ) {
  443. // called by SteamGameServer_Init. needed for games that require us to be connected to a server
  444. steamClient->SetLocalIPBinding( 0, 26901 );
  445. hSteamGameServerUser = steamClient->CreateLocalUser( &hSteamGameServerPipe, k_EAccountTypeGameServer );
  446. if ( !hSteamGameServerPipe || !hSteamGameServerUser ) {
  447. printf( "CreateLocalUser failed (2)\n" );
  448. goto funcEnd;
  449. }
  450.  
  451. steamGameServer = (ISteamGameServer012*)steamClient->GetISteamGameServer( hSteamGameServerUser, hSteamGameServerPipe, STEAMGAMESERVER_INTERFACE_VERSION_012 );
  452. if ( !steamGameServer ) {
  453. printf( "steamGameServer is null\n" );
  454. goto funcEnd;
  455. }
  456.  
  457. steamGameServer->InitGameServer( 0, 27015, MASTERSERVERUPDATERPORT_USEGAMESOCKETSHARE, k_unServerFlagSecure, 440, "3158168" );
  458. steamGameServer->SetProduct( "tf" );
  459. steamGameServer->SetGameDescription( "Team Fortress" );
  460. steamGameServer->SetModDir( "tf" );
  461. steamGameServer->SetDedicatedServer( false );
  462. steamGameServer->LogOnAnonymous();
  463. steamGameServer->SetMaxPlayerCount( 6 );
  464. steamGameServer->SetBotPlayerCount( 0 );
  465. steamGameServer->SetPasswordProtected( true );
  466. steamGameServer->SetRegion( "1" );
  467. steamGameServer->SetServerName( "Team Fortress 2" );
  468. steamGameServer->SetMapName( "ctf_2fort" );
  469. steamGameServer->SetGameData( "tf_mm_trusted:0,tf_mm_servermode:0,lobby:0,steamblocking:0" );
  470. steamGameServer->SetKeyValue( "tf_gamemode_ctf", "1" );
  471. steamGameServer->SetKeyValue( "sv_tags", "ctf" );
  472. steamGameServer->SetGameTags( "ctf" );
  473. //steamGameServer->EnableHeartbeats( true );
  474.  
  475. bGameServerInited = true;
  476. }
  477.  
  478. if ( !bPlayingOnServer ) {
  479. static HAuthTicket hAuthTicket = 0;
  480. if ( hAuthTicket ) {
  481. steamUser->CancelAuthTicket( hAuthTicket );
  482. steamGameServer->EndAuthSession( steamUser->GetSteamID() );
  483. hAuthTicket = 0;
  484. }
  485.  
  486. unsigned char ticket[1024];
  487. uint32 ticketSize;
  488. hAuthTicket = steamUser->GetAuthSessionTicket( ticket, sizeof( ticket ), &ticketSize );
  489. if ( hAuthTicket != k_HAuthTicketInvalid ) {
  490. EBeginAuthSessionResult beginAuthSessionResult = steamGameServer->BeginAuthSession( ticket, ticketSize, steamUser->GetSteamID() );
  491. if ( beginAuthSessionResult == k_EBeginAuthSessionResultOK )
  492. bPlayingOnServer = true;
  493. else
  494. printf( "BeginAuthSession failed (%d)\n", beginAuthSessionResult );
  495. }
  496. else {
  497. printf( "GetAuthSessionTicket failed\n" );
  498. }
  499. }
  500.  
  501. // process steam game server callbacks
  502. while ( Steam_BGetCallback( hSteamGameServerPipe, &callbackMsg ) )
  503. {
  504. switch ( callbackMsg.m_iCallback )
  505. {
  506. case ValidateAuthTicketResponse_t::k_iCallback:
  507. {
  508. ValidateAuthTicketResponse_t* validateAuthTicketResponse = (ValidateAuthTicketResponse_t*)callbackMsg.m_pubParam;
  509. if ( validateAuthTicketResponse->m_eAuthSessionResponse == k_EAuthSessionResponseOK ) {
  510. printf( "BeginAuthSession callback ok\n" );
  511. //steamGameServer->BUpdateUserData( validateAuthTicketResponse->m_SteamID, "Player", 0 );
  512. }
  513. else {
  514. printf( "BeginAuthSession callback failed (%d)\n", validateAuthTicketResponse->m_eAuthSessionResponse );
  515. bPlayingOnServer = false;
  516. }
  517. break;
  518. }
  519. //default:
  520. //printf( "Game server callback: %d\n", callbackMsg.m_iCallback );
  521. //break;
  522. }
  523.  
  524. Steam_FreeLastCallback( hSteamGameServerPipe );
  525. }
  526. }
  527. else {
  528. steamInventory->SendItemDropHeartbeat();
  529.  
  530. SteamInventoryResult_t steamInventoryResult;
  531. steamInventory->TriggerItemDrop( &steamInventoryResult, dropListDefinition );
  532.  
  533. steamInventory->DestroyResult( steamInventoryResult );
  534.  
  535. }
  536. }
  537.  
  538. Sleep( 1000 );
  539.  
  540.  
  541.  
  542.  
  543.  
  544.  
  545.  
  546. }
  547.  
  548. funcEnd:
  549. printf( "Press enter to exit...\n" );
  550. getchar();
  551. return 0;
  552. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement