/// Simple example of using the GW2 API for C++ novices. /*! -------------------------------------------------------------------------------- The example has been created in the most lazy way possible using copy/paste/modify from stackexchange.com and code.google.com/p/rapidjson/. The example has been tested with Visual Studio 2010 and Windows XP SP3. -------------------------------------------------------------------------------- To play with the code: 1) Create a new Win32 Console Application and copy the code into the main project file. 2) Download rapidjson and add the rapidjson include files to your projects include path. 3) Compile and run -------------------------------------------------------------------------------- Description Usage: wininet_test [-p [json_link]] If you use the -p option, the program reads any json data, beautifies it, and writes it to stdout. Default data to read, if not specified, is the world names https://api.guildwars2.com/v1/world_names.json. If you don't use the -p option, the program fetches the world names from https://api.guildwars2.com/v1/world_names.json and writes the results to standard output. The code can be refactored to make it prettier, but I am too lazy to do that. If you want mnore examples of how to use rapidjson read the source and/or read http://code.google.com/p/rapidjson/wiki/UserGuide -------------------------------------------------------------------------------- */ #include "stdafx.h" #include #include #include #include // For rapidjson document #include "document.h" // For rapidjson prettywrite #include "filestream.h" #include "prettywriter.h" using namespace rapidjson; void neterror(const char *id) { const DWORD error = GetLastError(); std::cerr << id << ": " << error << "." << std::endl; } HINTERNET netstart () { DWORD atype = INTERNET_OPEN_TYPE_DIRECT; const HINTERNET handle = InternetOpenW(0, atype, 0, 0, 0); if ( handle == 0 ) neterror("InternetOpen()"); return (handle); } void netstop ( HINTERNET object ) { const BOOL result = InternetCloseHandle(object); if ( result == FALSE ) neterror("InternetCloseHandle()"); } void (*conclose)( HINTERNET ) = netstop; HINTERNET conopen ( HINTERNET session, LPCWSTR url ) { const HINTERNET handle = InternetOpenUrlW(session, url, 0, 0, 0, 0); if ( handle == 0 ) neterror("InternetOpenUrl()"); return (handle); } int conread ( HINTERNET handle, std::vector& vect ) { static const DWORD SIZE = BUFSIZ * 10; DWORD bytes_read = 0; do { const DWORD old_size = vect.size(); // todo: we need to make sure that the allocation succeeds. we don't like exceptions. // todo: we need to make sure that the old_size + SIZE is less than max_size. vect.resize(old_size + SIZE); BOOL result = InternetReadFile(handle, &vect[old_size], SIZE, &bytes_read); if ( result == FALSE ) { neterror("InternetReadFile()"); return EXIT_FAILURE; } vect.resize(old_size + bytes_read); } while (bytes_read > 0); return EXIT_SUCCESS; } int check_object (const Value &v, Value::ConstMemberIterator& itr, SizeType i) { if(itr == v.MemberEnd()) { std::cerr << "Error: Array element " << i << " is an empty object." << std::endl; return (0); } if(!itr->value.IsString()) { std::cerr << "Error: Array element " << i << " has an object with a value that is not a string." << std::endl; return (0); } return (1); } // Fetch world names and write the results to standard output int proc_world_names(HINTERNET session) { const WCHAR URL[] = L"https://api.guildwars2.com/v1/world_names.json"; const HINTERNET connection = conopen(session, URL); if ( connection == 0 ) return (EXIT_FAILURE); std::vector vect; // Read the data and nul-terminate the data. int res = conread(connection, vect); vect.push_back(0); if(res == EXIT_SUCCESS) { Document d; d.Parse<0>(vect.data()); if(d.IsArray()) { for (SizeType i = 0; i < d.Size(); i++) { // rapidjson uses SizeType instead of size_t. const Value &v = d[i]; if(v.IsObject()) { Value::ConstMemberIterator itr = v.MemberBegin(); if(!check_object(v, itr, i)) break; std::cout << itr->name.GetString() << " = " << itr->value.GetString() << " "; ++itr; if(!check_object(v, itr, i)) break; std::cout << itr->name.GetString() << " = " << itr->value.GetString(); std::cout << std::endl; } else { std::cerr << "Error: Array element " << i << " is not an object." << std::endl; break; } } } else std::cerr << "Error: JSON returned is not an array." << std::endl; } conclose(connection); return (res); } int prettywrite(HINTERNET session, _TCHAR* arg) { //const WCHAR URL[] = L"https://render.guildwars2.com/file/02EFB1C5E11B2FF4B4AC25A84E2302D244C82AA3/66958.png"; //const WCHAR URL[] = L"https://tiles.guildwars2.com/1/1/7/48/49.jpg"; //const WCHAR URL[] = L"https://api.guildwars2.com/v1/event_names.json"; //const WCHAR URL[] = L"https://api.guildwars2.com/v1/map_floor.json?continent_id=1&floor=1"; //const WCHAR URL[] = L"https://api.guildwars2.com/v1/map_floor.json?continent_id=1&floor=5"; const WCHAR URL[] = L"https://api.guildwars2.com/v1/world_names.json"; const HINTERNET connection = conopen(session, (arg == NULL) ? URL : arg); if ( connection == 0 ) return (EXIT_FAILURE); std::vector vect; // Read the data and nul-terminate the data. int res = conread(connection, vect); vect.push_back(0); if(res == EXIT_SUCCESS) { Document d; d.Parse<0>(vect.data()); FileStream f(stdout); PrettyWriter writer(f); d.Accept(writer); } conclose(connection); return (res); } int _tmain(int argc, _TCHAR* argv[]) { int res = EXIT_FAILURE; const HINTERNET session = netstart(); if ( session == 0 ) return (res); if(argc >= 2 && (_tcslen(argv[1]) >= 2) && argv[1][0] == '-' && argv[1][1] == 'p') { res = prettywrite(session, argv[2]); } else { res = proc_world_names(session); } netstop(session); return (res); } #pragma comment ( lib, "Wininet.lib" )