diff --git a/Readme_release.txt b/Readme_release.txt index c0d11e0..a646f75 100644 --- a/Readme_release.txt +++ b/Readme_release.txt @@ -71,6 +71,7 @@ An example can be found in steam_settings.EXAMPLE that works with Killing Floor The items.json syntax is simple, you SHOULD validate your .json file before trying to run your game or you won't have any item in your inventory. Just look for "online json validator" on your web brower to valide your file. You can use https://steamdb.info/ to list items and attributes they have and put them into your .json. Keep in mind that some item are not valid to have in your inventory. For example, in PayDay2 all items below item_id 50000 will make your game crash. +items.json should contain all the item definitions for the game, default_items.json is the quantity of each item that you want a user to have initially in their inventory. By default the user will have no items. Leaderboards: By default the emulator assumes all leaderboards queried by the game (FindLeaderboard()) exist and creates them with the most common options (sort method descending, display type numeric) diff --git a/dll/base.cpp b/dll/base.cpp index 72ec0fa..32a0ab7 100644 --- a/dll/base.cpp +++ b/dll/base.cpp @@ -158,17 +158,17 @@ std::string get_lib_path() { int i = 0; struct dirent *ep; dp = opendir (dir.c_str()); - unsigned long long int p = (unsigned long long int)&get_lib_path; + uintptr_t p = (uintptr_t)&get_lib_path; if (dp != NULL) { while ((ep = readdir (dp))) { if (memcmp(ep->d_name, ".", 2) != 0 && memcmp(ep->d_name, "..", 3) != 0) { char *upper = NULL; - unsigned long long int lower_bound = strtoull(ep->d_name, &upper, 16); + uintptr_t lower_bound = strtoull(ep->d_name, &upper, 16); if (lower_bound) { ++upper; - unsigned long long int upper_bound = strtoull(upper, &upper, 16); + uintptr_t upper_bound = strtoull(upper, &upper, 16); if (upper_bound && (lower_bound < p && p < upper_bound)) { std::string path = dir + PATH_SEPARATOR + ep->d_name; char link[PATH_MAX] = {}; diff --git a/dll/local_storage.cpp b/dll/local_storage.cpp index d5d6d46..9f84317 100644 --- a/dll/local_storage.cpp +++ b/dll/local_storage.cpp @@ -127,6 +127,21 @@ bool Local_Storage::update_save_filenames(std::string folder) return true; } +bool Local_Storage::load_json(std::string full_path, nlohmann::json& json) +{ + return false; +} + +bool Local_Storage::load_json_file(std::string folder, std::string const&file, nlohmann::json& json) +{ + return false; +} + +bool Local_Storage::write_json_file(std::string folder, std::string const&file, nlohmann::json const& json) +{ + return false; +} + std::vector Local_Storage::get_filenames_path(std::string path) { return std::vector(); @@ -680,4 +695,68 @@ bool Local_Storage::update_save_filenames(std::string folder) return true; } +bool Local_Storage::load_json(std::string full_path, nlohmann::json& json) +{ + std::ifstream inventory_file(full_path); + // If there is a file and we opened it + if (inventory_file) + { + inventory_file.seekg(0, std::ios::end); + size_t size = inventory_file.tellg(); + std::string buffer(size, '\0'); + inventory_file.seekg(0); + // Read it entirely, if the .json file gets too big, + // I should look into this and split reads into smaller parts. + inventory_file.read(&buffer[0], size); + inventory_file.close(); + + try { + json = std::move(nlohmann::json::parse(buffer)); + PRINT_DEBUG("Loaded json \"%s\". Loaded %u items.\n", full_path.c_str(), json.size()); + return true; + } catch (std::exception& e) { + PRINT_DEBUG("Error while parsing \"%s\" json: %s\n", full_path.c_str(), e.what()); + } + } + else + { + PRINT_DEBUG("Couldn't open file \"%s\" to read json\n", full_path.c_str()); + } + + return false; +} + +bool Local_Storage::load_json_file(std::string folder, std::string const&file, nlohmann::json& json) +{ + if (!folder.empty() && folder.back() != *PATH_SEPARATOR) { + folder.append(PATH_SEPARATOR); + } + std::string inv_path = std::move(save_directory + appid + folder); + std::string full_path = inv_path + file; + + return load_json(full_path, json); +} + +bool Local_Storage::write_json_file(std::string folder, std::string const&file, nlohmann::json const& json) +{ + if (!folder.empty() && folder.back() != *PATH_SEPARATOR) { + folder.append(PATH_SEPARATOR); + } + std::string inv_path = std::move(save_directory + appid + folder); + std::string full_path = inv_path + file; + + create_directory(inv_path); + + std::ofstream inventory_file(full_path, std::ios::trunc | std::ios::out); + if (inventory_file) + { + inventory_file << std::setw(2) << json; + return true; + } + + PRINT_DEBUG("Couldn't open file \"%s\" to write json\n", full_path.c_str()); + + return false; +} + #endif diff --git a/dll/local_storage.h b/dll/local_storage.h index fb22db7..72aa5e2 100644 --- a/dll/local_storage.h +++ b/dll/local_storage.h @@ -59,6 +59,10 @@ public: std::string get_path(std::string folder); bool update_save_filenames(std::string folder); + + bool load_json(std::string full_path, nlohmann::json& json); + bool load_json_file(std::string folder, std::string const& file, nlohmann::json& json); + bool write_json_file(std::string folder, std::string const& file, nlohmann::json const& json); }; #endif diff --git a/dll/steam_inventory.h b/dll/steam_inventory.h index 2eec7fb..83ccee2 100644 --- a/dll/steam_inventory.h +++ b/dll/steam_inventory.h @@ -58,8 +58,7 @@ class Steam_Inventory : std::string items_db_file; std::once_flag load_items_flag; bool call_definition_update; - bool definition_update_called; - bool full_update_called; + bool item_definitions_loaded; struct Steam_Inventory_Requests* new_inventory_result(bool full_query=true, const SteamItemInstanceID_t* pInstanceIDs = NULL, uint32 unCountInstanceIDs = 0) { @@ -89,6 +88,25 @@ struct Steam_Inventory_Requests *get_inventory_result(SteamInventoryResult_t res return &(*request); } +void read_items_db() +{ + std::string items_db_path = Local_Storage::get_game_settings_path() + items_user_file; + PRINT_DEBUG("Items file path: %s\n", items_db_path.c_str()); + local_storage->load_json(items_db_path, defined_items); +} + +void read_inventory_db() +{ + // If we havn't got any inventory + if (!local_storage->load_json_file("", items_user_file, user_items)) + { + // Try to load a default one + std::string items_db_path = Local_Storage::get_game_settings_path() + items_default_file; + PRINT_DEBUG("Default items file path: %s\n", items_db_path.c_str()); + local_storage->load_json(items_db_path, user_items); + } +} + public: static void run_every_runcb_cb(void *object) @@ -99,7 +117,17 @@ static void run_every_runcb_cb(void *object) obj->RunCallbacks(); } -Steam_Inventory(class Settings *settings, class SteamCallResults *callback_results, class SteamCallBacks *callbacks, class RunEveryRunCB *run_every_runcb, std::string items_db_file_path) +Steam_Inventory(class Settings *settings, class SteamCallResults *callback_results, class SteamCallBacks *callbacks, class RunEveryRunCB *run_every_runcb, class Local_Storage *local_storage): + settings(settings), + callback_results(callback_results), + callbacks(callbacks), + run_every_runcb(run_every_runcb), + local_storage(local_storage), + defined_items(nlohmann::json::object()), + user_items(nlohmann::json::object()), + inventory_loaded(false), + call_definition_update(false), + item_definitions_loaded(false) { items_db_file = items_db_file_path; PRINT_DEBUG("Items file path: %s\n", items_db_file.c_str()); @@ -163,6 +191,7 @@ bool GetResultItems( SteamInventoryResult_t resultHandle, if (pOutItemsArray != nullptr) { + SteamItemDetails_t *items_array_base = pOutItemsArray; uint32 max_items = *punOutItemsArraySize; if (request->full_query) { @@ -175,22 +204,38 @@ bool GetResultItems( SteamInventoryResult_t resultHandle, pOutItemsArray->m_unFlags = k_ESteamItemNoTrade; ++pOutItemsArray; } - *punOutItemsArraySize = std::min(*punOutItemsArraySize, static_cast(items.size())); } else { for (auto &itemid : request->instance_ids) { if (!max_items) break; - pOutItemsArray->m_iDefinition = itemid; - pOutItemsArray->m_itemId = itemid; - pOutItemsArray->m_unQuantity = 1; - pOutItemsArray->m_unFlags = k_ESteamItemNoTrade; - ++pOutItemsArray; - --max_items; + auto it = user_items.find(std::to_string(itemid)); + if (it != user_items.end()) { + pOutItemsArray->m_iDefinition = itemid; + pOutItemsArray->m_itemId = itemid; + + try + { + pOutItemsArray->m_unQuantity = it->get(); + } + catch (...) + { + pOutItemsArray->m_unQuantity = 0; + } + pOutItemsArray->m_unFlags = k_ESteamItemNoTrade; + ++pOutItemsArray; + --max_items; + } } } + + *punOutItemsArraySize = pOutItemsArray - items_array_base; } else if (punOutItemsArraySize != nullptr) { - *punOutItemsArraySize = items.size(); + if (request->full_query) { + *punOutItemsArraySize = user_items.size(); + } else { + *punOutItemsArraySize = std::count_if(request->instance_ids.begin(), request->instance_ids.end(), [this](SteamItemInstanceID_t item_id){ return user_items.find(std::to_string(item_id)) != user_items.end();}); + } } PRINT_DEBUG("GetResultItems good\n"); @@ -278,8 +323,6 @@ bool GetAllItems( SteamInventoryResult_t *pResultHandle ) std::lock_guard lock(global_mutex); struct Steam_Inventory_Requests* request = new_inventory_result(); - if (!definition_update_called) call_definition_update = true; - if (pResultHandle != nullptr) *pResultHandle = request->inventory_result; @@ -531,7 +574,7 @@ bool LoadItemDefinitions() PRINT_DEBUG("LoadItemDefinitions\n"); std::lock_guard lock(global_mutex); - if (!definition_update_called) { + if (!item_definitions_loaded) { call_definition_update = true; } @@ -619,6 +662,7 @@ bool GetItemDefinitionProperty( SteamItemDef_t iDefinition, const char *pchPrope { *punValueBufferSizeOut = 0; PRINT_DEBUG("Attr %s not found for item %d\n", pchPropertyName, iDefinition); + return false; } } else // Pass a NULL pointer for pchPropertyName to get a comma - separated list of available property names. @@ -654,8 +698,11 @@ bool GetItemDefinitionProperty( SteamItemDef_t iDefinition, const char *pchPrope } } } + + return true; } - return true; + + return false; } @@ -782,35 +829,37 @@ bool SubmitUpdateProperties( SteamInventoryUpdateHandle_t handle, SteamInventory void RunCallbacks() { if (call_definition_update || inventory_requests.size()) { - std::call_once(load_items_flag, [&]() { - std::thread items_load_thread(read_items_db, items_db_file, &items, &items_loaded); - items_load_thread.detach(); - }); - } + if (!item_definitions_loaded) { + read_items_db(); + item_definitions_loaded = true; - if (items_loaded) { - if (call_definition_update) { + //only gets called once + //also gets called when getting items SteamInventoryDefinitionUpdate_t data = {}; callbacks->addCBResult(data.k_iCallback, &data, sizeof(data)); - call_definition_update = false; - definition_update_called = true; } + call_definition_update = false; + } + + if (inventory_requests.size() && !inventory_loaded) { + read_inventory_db(); + inventory_loaded = true; + } + + if (inventory_loaded) + { std::chrono::system_clock::time_point now = std::chrono::system_clock::now(); for (auto & r : inventory_requests) { if (!r.done && std::chrono::duration_cast>(now - r.time_created).count() > r.timeout) { if (r.full_query) { - if (!full_update_called) { - // SteamInventoryFullUpdate_t callbacks are triggered when GetAllItems - // successfully returns a result which is newer / fresher than the last - // known result. - //TODO: should this always be returned for each get all item calls? - struct SteamInventoryFullUpdate_t data; - data.m_handle = r.inventory_result; - callbacks->addCBResult(data.k_iCallback, &data, sizeof(data)); - full_update_called = true; - } + // SteamInventoryFullUpdate_t callbacks are triggered when GetAllItems + // successfully returns a result which is newer / fresher than the last + // known result. + struct SteamInventoryFullUpdate_t data; + data.m_handle = r.inventory_result; + callbacks->addCBResult(data.k_iCallback, &data, sizeof(data)); } { diff --git a/dll/steam_user_stats.cpp b/dll/steam_user_stats.cpp index 95138d6..217331a 100644 --- a/dll/steam_user_stats.cpp +++ b/dll/steam_user_stats.cpp @@ -31,7 +31,6 @@ unsigned int Steam_User_Stats::find_leaderboard(std::string name) void Steam_User_Stats::load_achievements_db() { - std::string file_path = Local_Storage::get_game_settings_path() + achievements_user_file; local_storage->load_json(file_path, defined_achievements); } @@ -40,12 +39,12 @@ void Steam_User_Stats::load_achievements() local_storage->load_json_file("", achievements_user_file, user_achievements); } -void Steam_User_Stats::save_achievements() +void save_achievements() { local_storage->write_json_file("", achievements_user_file, user_achievements); } -Steam_User_Stats::Steam_User_Stats(Settings* settings, Local_Storage* local_storage, class SteamCallResults* callback_results, class SteamCallBacks* callbacks) : +Steam_User_Stats::Steam_User_Stats(Settings *settings, Local_Storage *local_storage, class SteamCallResults *callback_results, class SteamCallBacks *callbacks): settings(settings), local_storage(local_storage), callback_results(callback_results), @@ -317,7 +316,7 @@ const char* Steam_User_Stats::GetAchievementDisplayAttribute(const char* pchName std::lock_guard lock(global_mutex); - if (strcmp(pchKey, "name") == 0) { + if (strcmp (pchKey, "name") == 0) { try { auto it = std::find_if(defined_achievements.begin(), defined_achievements.end(), [pchName](nlohmann::json& item) { return static_cast(item["name"]) == pchName; @@ -368,7 +367,7 @@ bool Steam_User_Stats::IndicateAchievementProgress(const char* pchName, uint32 n try { auto it = std::find_if(defined_achievements.begin(), defined_achievements.end(), [pchName](nlohmann::json& item) { return static_cast(item["name"]) == pchName; - }); + }); auto ach = user_achievements.find(pchName); if (it != defined_achievements.end()) { bool achieved = false; diff --git a/generate_game_infos/generate_game_infos.cpp b/generate_game_infos/generate_game_infos.cpp new file mode 100644 index 0000000..7cbcaf3 --- /dev/null +++ b/generate_game_infos/generate_game_infos.cpp @@ -0,0 +1,462 @@ +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +class CurlGlobal +{ + bool _init; + + CurlGlobal() :_init(false) {} + + ~CurlGlobal() { cleanup(); } + +public: + static CurlGlobal& Inst() + { + static CurlGlobal _this; + return _this; + } + + CURLcode init(long flags = CURL_GLOBAL_DEFAULT) { return curl_global_init(flags); } + void cleanup() + { + if (_init) + { + curl_global_cleanup(); + _init = false; + } + } +}; + +class CurlEasy +{ + CURL* _me; + bool _init; + std::string _buffer; + + static int writer(char* data, size_t size, size_t nmemb, + CurlEasy *_this) + { + if (_this == nullptr) + return 0; + + _this->_buffer.append(data, size * nmemb); + + return size * nmemb; + } + +public: + CurlEasy() :_me(nullptr), _init(false) {} + ~CurlEasy() { cleanup(); } + + bool init() + { + _init = (_me = curl_easy_init()) != nullptr; + if (_init) + { + if (curl_easy_setopt(_me, CURLOPT_WRITEFUNCTION, writer) != CURLE_OK) + { + cleanup(); + return false; + } + + if (curl_easy_setopt(_me, CURLOPT_WRITEDATA, this) != CURLE_OK) + { + cleanup(); + return false; + } + } + return _init; + } + + void cleanup() + { + if (_init) + { + curl_easy_cleanup(_me); + } + } + + CURLcode set_url(const std::string& url) + { + return curl_easy_setopt(_me, CURLOPT_URL, url.c_str()); + } + + CURLcode skip_verifypeer(bool skip = true) + { + return curl_easy_setopt(_me, CURLOPT_SSL_VERIFYPEER, skip ? 0L : 1L); + } + + CURLcode skip_verifhost(bool skip = true) + { + return curl_easy_setopt(_me, CURLOPT_SSL_VERIFYHOST, skip ? 0L : 1L); + } + + CURLcode connect_only(bool connect = true) + { + return curl_easy_setopt(_me, CURLOPT_CONNECT_ONLY, connect ? 1L : 0L); + } + + CURLcode perform() + { + _buffer.clear(); + return curl_easy_perform(_me); + } + + CURLcode recv(void *buffer, size_t buflen, size_t* read_len) + { + return curl_easy_recv(_me, buffer, buflen, read_len); + } + + CURLcode get_html_code(long &code) + { + return curl_easy_getinfo(_me, CURLINFO_RESPONSE_CODE, &code); + } + + std::string const& get_answer() const { return _buffer; } +}; + +// Get all steam appid with their name: http://api.steampowered.com/ISteamApps/GetAppList/v2/ +// Steam storefront webapi: https://wiki.teamfortress.com/wiki/User:RJackson/StorefrontAPI +// http://api.steampowered.com/ISteamUserStats/GetSchemaForGame/v2/?key=&appid= +/* +{ + "game" : { + "gameName" : "", + "availableGameStats" : { + "achievements" : { + ("" : { + "name" : "achievement_name", + "displayName" : "achievement name on screen", + "hidden" : (0|1), + ["description" : "",] + "icon" : "", + "icongray" : "" + }, + ...) + } + } + } +} +*/ +// Get appid infos: http://store.steampowered.com/api/appdetails/?appids=218620 +/* +"appid" : { + "success" : (true|false), + (success == true "data" : { + ... + "name" : "", + "steam_appid" : , + (OPT "dlc" : [, ]), + "header_image" : "" <-- Use this in the overlay ? + (OPT "achievements" : { + "total" : + }), + "background" : "" <-- Use this as the overlay background ? + (OPT "packages" : [, ]) + }) +} +*/ +// --------------------------------- +// -- Special thanks to psychonic -- +// --------------------------------- +// Get game items definition digest (Phase1): https://api.steampowered.com/IInventoryService/GetItemDefMeta/v1?key=&appid=218620 +/* +{ + "response": { + "modified": 1566848385, + "digest": "3CDFC1CC1AC2B0D55D12C1C130F4294BDD6DF8D0" + } +} +*/ + +// Get game items definition: https://api.steampowered.com/IGameInventory/GetItemDefArchive/v0001?appid=218620&digest= +/* +[ + { + "appid":"218620", + "itemdefid":"0", + "Timestamp":"2016-04-08T18:00:21.3643085Z", + "modified":"20160408T180021Z", + "date_created":"20160408T180021Z", + "type":"", + "display_type":"", + "name":"", + "quantity":0, + "description":"", + "tradable":false, + "marketable":false, + "commodity":false, + "drop_interval":0, + "drop_max_per_window":0, + "workshopid":"0" + }, + { + "appid":"218620", + "itemdefid":"50002", + "Timestamp":"2015-11-13T16:01:18.0338618Z", + "modified":"20151113T160117Z", + "date_created":"20151113T160117Z", + "type":"item", + "display_type":"", + "name":"Sputnik Safe", + "quantity":0, + "description":"[color=#2360D8]THE JUDGE SHOTGUN | Pixel [/color]\n[color=#2360D8]KOBUS 90 SUBMACHINE GUN | Red Stars[/color]\n[color=#2360D8]PLAINSRIDER BOW | Arctic Plains[/color]\n[color=#2360D8]GRUBER KURZ PISTOL | Little Leopard[/color]\n[color=#2360D8]HRL-7 ROCKET LAUNCHER | Headline[/color]\n[color=#2360D8]LOCOMOTIVE 12G SHOTGUN | Cosmonaut[/color]\n[color=#9900FF]FLAMETHROWER | St. Basil[/color]\n[color=#9900FF]JP36 RIFLE | Ice Leopard [/color]\n[color=#9900FF]CAR-4 RIFLE | Stripe On[/color]\n[color=#9900FF]BRONCO .44 REVOLVER | Black Bull[/color]\n[color=#FF00FF]BERNETTI 9 PISTOL | Angry Bear[/color]\n[color=#FF00FF]THANATOS .50 CAL SNIPER RIFLE | Matrjoschka[/color]\n[color=#FF00FF]M308 RIFLE | Helmet Space Program[/color]\n[color=#FF0000]CLARION RIFLE | Breaching Owl[/color]\n[color=#FF0000]MOSCONI 12G SHOTGUN | Bullet Bear Gun[/color]\n[color=#FFAA00]or an exceedingly rare special item![/color]", + "icon_url":"http://media.overkillsoftware.com/economy42gF2Y/safes_weapon_01.png", + "icon_url_large":"http://media.overkillsoftware.com/economy42gF2Y/safes_weapon_01.png", + "store_tags":"safe;sputnik safe;", + "tradable":true, + "marketable":true, + "commodity":false, + "drop_interval":0, + "drop_max_per_window":0, + "workshopid":"0", + "dsl_bonus":"false", + "item_name":"weapon_01", + "item_slot":"safes" + } +*/ + +#ifdef max +#undef max +#endif + +std::string steam_apikey; +std::string app_id; + +#if defined(WIN32) || defined(_WIN32) +#include + +static bool create_directory(std::string const& strPath) +{ + DWORD dwAttrib = GetFileAttributesA(strPath.c_str()); + + if (dwAttrib != INVALID_FILE_ATTRIBUTES && dwAttrib & FILE_ATTRIBUTE_DIRECTORY) + return true; + + return CreateDirectoryA(strPath.c_str(), NULL); +} +#elif defined(__linux__) +#include +#include +#include + +static bool create_directory(std::string const& strPath) +{ + struct stat sb; + + if (stat(strPath.c_str(), &sb) != 0) + { + return mkdir(strPath.c_str(), 0755) == 0; + } + if (S_ISDIR(sb.st_mode)) + return true; + + return false; +} + +#endif + +static void generate_achievements(CurlEasy &easy) +{ + std::string url = "http://api.steampowered.com/ISteamUserStats/GetSchemaForGame/v2/?key="; + url += steam_apikey; + url += "&appid="; + url += app_id; + easy.set_url(url); + easy.perform(); + try + { + std::ofstream ach_file("achievements.json", std::ios::trunc | std::ios::out); + nlohmann::json json = nlohmann::json::parse(easy.get_answer()); + nlohmann::json output_json = nlohmann::json::array(); + + bool first = true; + int i = 0; + for (auto& item : json["game"]["availableGameStats"]["achievements"].items()) + { + output_json[i]["name"] = item.value()["name"]; + output_json[i]["displayName"] = item.value()["displayName"]; + output_json[i]["hidden"] = std::to_string(item.value()["hidden"].get()); + try + { + if( !item.value()["description"].is_null() ) + output_json[i]["description"] = item.value()["description"]; + else + output_json[i]["description"] = ""; + } + catch (...) + { + output_json[i]["description"] = ""; + } + + { + std::string icon_path = "images/" + item.value()["name"].get() + ".jpg"; + std::ofstream achievement_icon(icon_path, std::ios::out | std::ios::trunc | std::ios::binary); + if (!achievement_icon) + { + std::cerr << "Cannot create achievement icon \"" << icon_path << "\"" << std::endl; + return; + } + easy.set_url(item.value()["icon"]); + easy.perform(); + + std::string picture = easy.get_answer(); + achievement_icon.write(picture.c_str(), picture.length()); + + output_json[i]["icon"] = icon_path; + + } + { + std::string icon_path = "images/" + item.value()["name"].get() + "_gray.jpg"; + std::ofstream achievement_icon(icon_path, std::ios::out | std::ios::trunc | std::ios::binary); + if (!achievement_icon) + { + std::cerr << "Cannot create achievement icon \"" << icon_path << "\"" << std::endl; + return; + } + easy.set_url(item.value()["icongray"]); + easy.perform(); + + std::string picture = easy.get_answer(); + achievement_icon.write(picture.c_str(), picture.length()); + + output_json[i]["icongray"] = icon_path; + } + ++i; + } + ach_file << std::setw(2) << output_json; + } + catch (std::exception& e) + { + std::cerr << "Failed to get infos: "; + long code; + if (easy.get_html_code(code) == CURLE_OK && code == 403) + { + std::cerr << "Error in webapi key"; + } + else + { + std::cerr << "Error while parsing json. Try to go at " << url << " and see what you can do to build your achivements.json"; + } + std::cerr << std::endl; + } +} + +static void generate_items(CurlEasy& easy) +{ + std::string url = "http://api.steampowered.com/IInventoryService/GetItemDefMeta/v1?key="; + url += steam_apikey; + url += "&appid="; + url += app_id; + + easy.set_url(url); + easy.perform(); + + try + { + nlohmann::json json = nlohmann::json::parse(easy.get_answer()); + std::string digest = json["response"]["digest"]; + + url = "http://api.steampowered.com/IGameInventory/GetItemDefArchive/v0001?appid="; + url += app_id; + url += "&digest="; + url += digest; + + easy.set_url(url); + easy.perform(); + + nlohmann::json item_json = nlohmann::json::object(); + nlohmann::json default_item_json = nlohmann::json::object(); + + json = nlohmann::json::parse(easy.get_answer()); + std::ofstream items_file("items.json", std::ios::trunc | std::ios::out); + std::ofstream default_items_file("default_items.json", std::ios::trunc | std::ios::out); + + for (auto &i : json) + { + for (auto j = i.begin(); j != i.end(); ++j) + { + //if (j.key() == "itemdefid") + //{ + // j.value() = std::stoll(j.value().get()); + //} + //else + { + nlohmann::json& v = j.value(); + switch (v.type()) + { + case nlohmann::json::value_t::boolean: + v = (v.get() ? "true" : "false"); + break; + + case nlohmann::json::value_t::number_float: + v = std::to_string(v.get()); + break; + + case nlohmann::json::value_t::number_integer: + v = std::to_string(v.get()); + break; + + case nlohmann::json::value_t::number_unsigned: + v = std::to_string(v.get()); + break; + } + } + } + item_json[i["itemdefid"].get()] = i; + default_item_json[i["itemdefid"].get()] = 1; + } + + items_file << std::setw(2) << item_json; + default_items_file << std::setw(2) << default_item_json; + } + catch (std::exception& e) + { + std::cerr << "Failed to get infos: "; + long code; + if (easy.get_html_code(code) == CURLE_OK && code == 403) + { + std::cerr << "Error in webapi key"; + } + else + { + std::cerr << "Error while parsing json. Try to go at " << url << " and see what you can do to build your items.json"; + } + std::cerr << std::endl; + } +} + +int main() +{ + if (!create_directory("images")) + { + std::cerr << "Cannot create directory \"images\"" << std::endl; + return -1; + } + + CurlGlobal& cglobal = CurlGlobal::Inst(); + cglobal.init(); + + CurlEasy easy; + if (easy.init()) + { + std::cout << "Enter the game appid: "; + std::cin >> app_id; + std::cout << "Enter your webapi key: "; + std::cin.clear(); + std::cin.ignore(std::numeric_limits::max(), '\n'); + std::cin >> steam_apikey; + + generate_achievements(easy); + generate_items(easy); + } +} + \ No newline at end of file