parent
ced9b77afc
commit
ea4588f442
|
@ -0,0 +1,45 @@
|
||||||
|
#! /bin/bash
|
||||||
|
|
||||||
|
# Build type (Debug or Release)
|
||||||
|
BUILD_TYPE="$1"
|
||||||
|
# where to build protobuf, must be win32 or win64
|
||||||
|
OUT_DIR="$2"
|
||||||
|
|
||||||
|
[ "$OUT_DIR" != "win32" -a "$OUT_DIR" != "win64" -a "$OUT_DIR" != "x86" -a "$OUT_DIR" != "x64" ] && echo "The output dir must be 'Win32', 'Win64', 'x86' or 'x64'" && exit 1
|
||||||
|
[ "$BUILD_TYPE" != "Debug" -a "$BUILD_TYPE" != "Release" ] && echo "The build type must be 'Debug' or 'Release'" && exit 1
|
||||||
|
|
||||||
|
# apt install libssl1.0-dev libssl1.0-dev:i386
|
||||||
|
|
||||||
|
# My variable to decide if we build x86 or x64 in CMakeLists.txt
|
||||||
|
if [ "$OUT_DIR" == "win32" -o "$OUT_DIR" == "x86" ]; then
|
||||||
|
custom_arch_var="-DX86=ON"
|
||||||
|
else
|
||||||
|
custom_arch_var="-DX64=ON"
|
||||||
|
fi
|
||||||
|
|
||||||
|
build_type="-DCMAKE_BUILD_TYPE=${BUILD_TYPE}"
|
||||||
|
|
||||||
|
build_http="-DHTTP_ONLY=ON"
|
||||||
|
build_exe="-DBUILD_CURL_EXE=OFF"
|
||||||
|
build_shared="-DBUILD_SHARED_LIBS=OFF"
|
||||||
|
build_testing="-DBUILD_TESTING=OFF"
|
||||||
|
|
||||||
|
args=()
|
||||||
|
args+=($build_http)
|
||||||
|
args+=($build_exe)
|
||||||
|
args+=($build_shared)
|
||||||
|
args+=($build_testing)
|
||||||
|
args+=($build_type)
|
||||||
|
args+=($custom_arch_var)
|
||||||
|
|
||||||
|
# EXTRA_CMAKE_ENV is set by setup_clang_env.sh to build for windows.
|
||||||
|
# You must run setup_clang_env.sh before calling this script if you build for windows.
|
||||||
|
|
||||||
|
rm -rf "curl/$OUT_DIR" &&
|
||||||
|
mkdir "curl/$OUT_DIR" &&
|
||||||
|
cd "curl/$OUT_DIR" &&
|
||||||
|
echo "cmake -G \"Unix Makefiles\" $EXTRA_CMAKE_ENV \"${args[@]}\" .." &&
|
||||||
|
cmake -G "Unix Makefiles" $EXTRA_CMAKE_ENV "${args[@]}" .. &&
|
||||||
|
make -j${JOBS-2} || exit 1
|
||||||
|
|
||||||
|
exit 0
|
|
@ -0,0 +1,79 @@
|
||||||
|
#CMAKE_TOOLCHAIN_FILE
|
||||||
|
|
||||||
|
project(goldberg_emulator_protobuf)
|
||||||
|
cmake_minimum_required(VERSION 3.0)
|
||||||
|
|
||||||
|
if(WIN32)
|
||||||
|
# Detect arch on Windows
|
||||||
|
if( ${CMAKE_SIZEOF_VOID_P} EQUAL 8)
|
||||||
|
set(X64 ON)
|
||||||
|
else()
|
||||||
|
set(X86 ON)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(MSVC)
|
||||||
|
add_definitions(-D_CRT_SECURE_NO_WARNINGS)
|
||||||
|
set(CMAKE_CONFIGURATION_TYPES "Debug;Release" CACHE STRING "" FORCE)
|
||||||
|
else()
|
||||||
|
add_compile_options(-std=c++11)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
set(win_libs Iphlpapi ws2_32)
|
||||||
|
if(X64)
|
||||||
|
set(STEAM_NAME steam_api64)
|
||||||
|
elseif(X86)
|
||||||
|
set(STEAM_NAME steam_api)
|
||||||
|
else()
|
||||||
|
message(FATAL_ERROR "Arch unknown")
|
||||||
|
endif()
|
||||||
|
elseif(APPLE)
|
||||||
|
message(FATAL_ERROR "No CMake for Apple")
|
||||||
|
else()
|
||||||
|
if(X64)
|
||||||
|
set(CMAKE_C_FLAGS "-m64")
|
||||||
|
set(CMAKE_CXX_FLAGS "-m64")
|
||||||
|
elseif(X86)
|
||||||
|
set(CMAKE_C_FLAGS "-m32")
|
||||||
|
set(CMAKE_CXX_FLAGS "-m32")
|
||||||
|
else()
|
||||||
|
message(FATAL_ERROR "Arch unknown")
|
||||||
|
endif()
|
||||||
|
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fvisibility=hidden -fPIC")
|
||||||
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fvisibility=hidden -fPIC")
|
||||||
|
|
||||||
|
endif()
|
||||||
|
|
||||||
|
set(CMAKE_CXX_STANDARD 11)
|
||||||
|
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||||
|
set(CMAKE_CXX_EXTENSIONS OFF)
|
||||||
|
|
||||||
|
set(CURL_VERSION "7.65.3")
|
||||||
|
set(CURL_DIR ${CMAKE_CURRENT_SOURCE_DIR})
|
||||||
|
set(CURL_RELEASE_URL "https://curl.haxx.se/download/curl-${CURL_VERSION}.tar.xz")
|
||||||
|
set(CURL_SRC curl-src)
|
||||||
|
|
||||||
|
if( NOT EXISTS ${CURL_DIR}/${CURL_SRC} )
|
||||||
|
file(
|
||||||
|
DOWNLOAD ${CURL_RELEASE_URL} ${CURL_DIR}/curl.tar.xz
|
||||||
|
SHOW_PROGRESS
|
||||||
|
EXPECTED_HASH MD5=7bd5b2ebfd3f591034eb8b55314d8c02
|
||||||
|
)
|
||||||
|
|
||||||
|
if( NOT EXISTS ${CURL_DIR}/curl.tar.xz )
|
||||||
|
message(FATAL_ERROR "Download of curl failed")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
execute_process(
|
||||||
|
COMMAND ${CMAKE_COMMAND} -E tar -xf curl.tar.xz
|
||||||
|
WORKING_DIRECTORY ${CURL_DIR}
|
||||||
|
)
|
||||||
|
|
||||||
|
file(REMOVE ${CURL_DIR}/curl.tar.xz)
|
||||||
|
file(RENAME ${CURL_DIR}/curl-${CURL_VERSION} "${CURL_SRC}")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
|
||||||
|
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
|
||||||
|
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
|
||||||
|
|
||||||
|
add_subdirectory(${CURL_DIR}/${CURL_SRC})
|
|
@ -335,6 +335,7 @@ Steam_Client::Steam_Client()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string achievements_db_file_path = (Local_Storage::get_game_settings_path() + "achievements.json");
|
||||||
std::string items_db_file_path = (Local_Storage::get_game_settings_path() + "items.json");
|
std::string items_db_file_path = (Local_Storage::get_game_settings_path() + "items.json");
|
||||||
|
|
||||||
network = new Networking(settings_server->get_local_steam_id(), appid, port, &custom_broadcasts);
|
network = new Networking(settings_server->get_local_steam_id(), appid, port, &custom_broadcasts);
|
||||||
|
@ -352,7 +353,7 @@ Steam_Client::Steam_Client()
|
||||||
steam_utils = new Steam_Utils(settings_client, callback_results_client);
|
steam_utils = new Steam_Utils(settings_client, callback_results_client);
|
||||||
steam_matchmaking = new Steam_Matchmaking(settings_client, network, callback_results_client, callbacks_client, run_every_runcb);
|
steam_matchmaking = new Steam_Matchmaking(settings_client, network, callback_results_client, callbacks_client, run_every_runcb);
|
||||||
steam_matchmaking_servers = new Steam_Matchmaking_Servers(settings_client, network);
|
steam_matchmaking_servers = new Steam_Matchmaking_Servers(settings_client, network);
|
||||||
steam_user_stats = new Steam_User_Stats(settings_client, local_storage, callback_results_client, callbacks_client);
|
steam_user_stats = new Steam_User_Stats(settings_client, local_storage, callback_results_client, callbacks_client, achievements_db_file_path);
|
||||||
steam_apps = new Steam_Apps(settings_client, callback_results_client);
|
steam_apps = new Steam_Apps(settings_client, callback_results_client);
|
||||||
steam_networking = new Steam_Networking(settings_client, network, callbacks_client, run_every_runcb);
|
steam_networking = new Steam_Networking(settings_client, network, callbacks_client, run_every_runcb);
|
||||||
steam_remote_storage = new Steam_Remote_Storage(settings_client, local_storage, callback_results_client);
|
steam_remote_storage = new Steam_Remote_Storage(settings_client, local_storage, callback_results_client);
|
||||||
|
|
|
@ -17,13 +17,16 @@
|
||||||
|
|
||||||
#include "base.h"
|
#include "base.h"
|
||||||
|
|
||||||
|
#include <iomanip>
|
||||||
|
#include <fstream>
|
||||||
|
#include "../json/json.hpp"
|
||||||
|
|
||||||
struct Steam_Leaderboard {
|
struct Steam_Leaderboard {
|
||||||
std::string name;
|
std::string name;
|
||||||
ELeaderboardSortMethod sort_method;
|
ELeaderboardSortMethod sort_method;
|
||||||
ELeaderboardDisplayType display_type;
|
ELeaderboardDisplayType display_type;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
class Steam_User_Stats :
|
class Steam_User_Stats :
|
||||||
public ISteamUserStats003,
|
public ISteamUserStats003,
|
||||||
public ISteamUserStats004,
|
public ISteamUserStats004,
|
||||||
|
@ -41,6 +44,9 @@ public ISteamUserStats
|
||||||
class SteamCallBacks *callbacks;
|
class SteamCallBacks *callbacks;
|
||||||
std::vector<struct Steam_Leaderboard> leaderboards;
|
std::vector<struct Steam_Leaderboard> leaderboards;
|
||||||
|
|
||||||
|
std::string db_file_path;
|
||||||
|
nlohmann::json achievements;
|
||||||
|
|
||||||
unsigned int find_leaderboard(std::string name)
|
unsigned int find_leaderboard(std::string name)
|
||||||
{
|
{
|
||||||
unsigned index = 1;
|
unsigned index = 1;
|
||||||
|
@ -52,13 +58,41 @@ unsigned int find_leaderboard(std::string name)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public:
|
void load_achievements()
|
||||||
Steam_User_Stats(Settings *settings, Local_Storage *local_storage, class SteamCallResults *callback_results, class SteamCallBacks *callbacks)
|
|
||||||
{
|
{
|
||||||
this->local_storage = local_storage;
|
std::ifstream achs_file(db_file_path);
|
||||||
this->settings = settings;
|
|
||||||
this->callback_results = callback_results;
|
if (achs_file)
|
||||||
this->callbacks = callbacks;
|
{
|
||||||
|
achs_file.seekg(0, std::ios::end);
|
||||||
|
size_t size = achs_file.tellg();
|
||||||
|
std::string buffer(size, '\0');
|
||||||
|
achs_file.seekg(0);
|
||||||
|
// Read it entirely, if the .json file gets too big,
|
||||||
|
// I should look into this and split reads into smaller parts.
|
||||||
|
achs_file.read(&buffer[0], size);
|
||||||
|
achs_file.close();
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
achievements = nlohmann::json::parse(buffer);
|
||||||
|
}
|
||||||
|
catch (std::exception &e)
|
||||||
|
{
|
||||||
|
PRINT_DEBUG("(Achievements): Error while parsing json: %s\n", e.what());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
Steam_User_Stats(Settings *settings, Local_Storage *local_storage, class SteamCallResults *callback_results, class SteamCallBacks *callbacks, std::string const&achievements_db_file_path):
|
||||||
|
settings(settings),
|
||||||
|
local_storage(local_storage),
|
||||||
|
callback_results(callback_results),
|
||||||
|
callbacks(callbacks),
|
||||||
|
db_file_path(achievements_db_file_path)
|
||||||
|
{
|
||||||
|
load_achievements();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ask the server to send down this user's data and achievements for this game
|
// Ask the server to send down this user's data and achievements for this game
|
||||||
|
@ -155,19 +189,65 @@ bool GetAchievement( const char *pchName, bool *pbAchieved )
|
||||||
{
|
{
|
||||||
//TODO: these achievement functions need to load a list of achievements from somewhere, return false so that kf2 doesn't loop endlessly
|
//TODO: these achievement functions need to load a list of achievements from somewhere, return false so that kf2 doesn't loop endlessly
|
||||||
PRINT_DEBUG("GetAchievement %s\n", pchName);
|
PRINT_DEBUG("GetAchievement %s\n", pchName);
|
||||||
*pbAchieved = false;
|
try
|
||||||
|
{
|
||||||
|
auto it = std::find_if(achievements.begin(), achievements.end(), [pchName]( nlohmann::json &item ) {
|
||||||
|
return static_cast<std::string const&>(item["name"]) == pchName;
|
||||||
|
});
|
||||||
|
if (it != achievements.end())
|
||||||
|
{
|
||||||
|
*pbAchieved = it.value()["earned"];
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (...)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SetAchievement( const char *pchName )
|
bool SetAchievement( const char *pchName )
|
||||||
{
|
{
|
||||||
PRINT_DEBUG("SetAchievement %s\n", pchName);
|
PRINT_DEBUG("SetAchievement %s\n", pchName);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
auto it = std::find_if(achievements.begin(), achievements.end(), [pchName](nlohmann::json& item) {
|
||||||
|
return static_cast<std::string const&>(item["name"]) == pchName;
|
||||||
|
});
|
||||||
|
if (it != achievements.end())
|
||||||
|
{
|
||||||
|
it.value()["earned"] = 1;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (...)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ClearAchievement( const char *pchName )
|
bool ClearAchievement( const char *pchName )
|
||||||
{
|
{
|
||||||
PRINT_DEBUG("ClearAchievement %s\n", pchName);
|
PRINT_DEBUG("ClearAchievement %s\n", pchName);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
auto it = std::find_if(achievements.begin(), achievements.end(), [pchName](nlohmann::json& item) {
|
||||||
|
return static_cast<std::string const&>(item["name"]) == pchName;
|
||||||
|
});
|
||||||
|
if (it != achievements.end())
|
||||||
|
{
|
||||||
|
it.value()["earned"] = 0;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (...)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -178,7 +258,26 @@ bool ClearAchievement( const char *pchName )
|
||||||
bool GetAchievementAndUnlockTime( const char *pchName, bool *pbAchieved, uint32 *punUnlockTime )
|
bool GetAchievementAndUnlockTime( const char *pchName, bool *pbAchieved, uint32 *punUnlockTime )
|
||||||
{
|
{
|
||||||
PRINT_DEBUG("GetAchievementAndUnlockTime\n");
|
PRINT_DEBUG("GetAchievementAndUnlockTime\n");
|
||||||
|
try
|
||||||
|
{
|
||||||
|
auto it = std::find_if(achievements.begin(), achievements.end(), [pchName](nlohmann::json& item) {
|
||||||
|
return static_cast<std::string const&>(item["name"]) == pchName;
|
||||||
|
});
|
||||||
|
if (it != achievements.end())
|
||||||
|
{
|
||||||
|
*pbAchieved = it.value()["earned"].get<int>();
|
||||||
|
*punUnlockTime = std::time(NULL);
|
||||||
|
//*punUnlockTime = it.value()["time_earned"].get<uint32>();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (...)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
*pbAchieved = false;
|
*pbAchieved = false;
|
||||||
|
*punUnlockTime = 0;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -195,6 +294,12 @@ bool StoreStats()
|
||||||
PRINT_DEBUG("StoreStats\n");
|
PRINT_DEBUG("StoreStats\n");
|
||||||
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
||||||
|
|
||||||
|
std::ofstream achiev_file(db_file_path, std::ios::trunc | std::ios::out);
|
||||||
|
if (achiev_file)
|
||||||
|
{
|
||||||
|
achiev_file << std::setw(2) << achievements;
|
||||||
|
}
|
||||||
|
|
||||||
UserStatsStored_t data;
|
UserStatsStored_t data;
|
||||||
data.m_nGameID = settings->get_local_game_id().ToUint64();
|
data.m_nGameID = settings->get_local_game_id().ToUint64();
|
||||||
data.m_eResult = k_EResultOK;
|
data.m_eResult = k_EResultOK;
|
||||||
|
@ -222,18 +327,56 @@ int GetAchievementIcon( const char *pchName )
|
||||||
const char * GetAchievementDisplayAttribute( const char *pchName, const char *pchKey )
|
const char * GetAchievementDisplayAttribute( const char *pchName, const char *pchKey )
|
||||||
{
|
{
|
||||||
PRINT_DEBUG("GetAchievementDisplayAttribute %s %s\n", pchName, pchKey);
|
PRINT_DEBUG("GetAchievementDisplayAttribute %s %s\n", pchName, pchKey);
|
||||||
return ""; //TODO
|
|
||||||
|
|
||||||
if (strcmp (pchKey, "name") == 0) {
|
if (strcmp (pchKey, "name") == 0) {
|
||||||
return "Achievement Name";
|
try
|
||||||
|
{
|
||||||
|
auto it = std::find_if(achievements.begin(), achievements.end(), [pchName](nlohmann::json& item) {
|
||||||
|
return static_cast<std::string const&>(item["name"]) == pchName;
|
||||||
|
});
|
||||||
|
if (it != achievements.end())
|
||||||
|
{
|
||||||
|
return it.value()["displayName"].get<std::string>().c_str();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (...)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (strcmp (pchKey, "desc") == 0) {
|
if (strcmp (pchKey, "desc") == 0) {
|
||||||
return "Achievement Description";
|
try
|
||||||
|
{
|
||||||
|
auto it = std::find_if(achievements.begin(), achievements.end(), [pchName](nlohmann::json& item) {
|
||||||
|
return static_cast<std::string const&>(item["name"]) == pchName;
|
||||||
|
});
|
||||||
|
if (it != achievements.end())
|
||||||
|
{
|
||||||
|
return it.value()["description"].get<std::string>().c_str();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (...)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (strcmp (pchKey, "hidden") == 0) {
|
if (strcmp (pchKey, "hidden") == 0) {
|
||||||
return "0";
|
try
|
||||||
|
{
|
||||||
|
auto it = std::find_if(achievements.begin(), achievements.end(), [pchName](nlohmann::json& item) {
|
||||||
|
return static_cast<std::string const&>(item["name"]) == pchName;
|
||||||
|
});
|
||||||
|
if (it != achievements.end())
|
||||||
|
{
|
||||||
|
return (it.value()["hidden"].get<int>() ? "1" : "0");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (...)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return "";
|
return "";
|
||||||
|
@ -253,13 +396,21 @@ bool IndicateAchievementProgress( const char *pchName, uint32 nCurProgress, uint
|
||||||
uint32 GetNumAchievements()
|
uint32 GetNumAchievements()
|
||||||
{
|
{
|
||||||
PRINT_DEBUG("GetNumAchievements\n");
|
PRINT_DEBUG("GetNumAchievements\n");
|
||||||
return 0;
|
return achievements.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get achievement name iAchievement in [0,GetNumAchievements)
|
// Get achievement name iAchievement in [0,GetNumAchievements)
|
||||||
const char * GetAchievementName( uint32 iAchievement )
|
const char * GetAchievementName( uint32 iAchievement )
|
||||||
{
|
{
|
||||||
PRINT_DEBUG("GetAchievementName\n");
|
PRINT_DEBUG("GetAchievementName\n");
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return static_cast<std::string const&>(achievements[iAchievement]["name"]).c_str();
|
||||||
|
}
|
||||||
|
catch (...)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -276,6 +427,9 @@ SteamAPICall_t RequestUserStats( CSteamID steamIDUser )
|
||||||
PRINT_DEBUG("Steam_User_Stats::RequestUserStats %llu\n", steamIDUser.ConvertToUint64());
|
PRINT_DEBUG("Steam_User_Stats::RequestUserStats %llu\n", steamIDUser.ConvertToUint64());
|
||||||
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
||||||
|
|
||||||
|
// Enable this to allow hot reload achievements status
|
||||||
|
//load_achievements();
|
||||||
|
|
||||||
UserStatsReceived_t data;
|
UserStatsReceived_t data;
|
||||||
data.m_nGameID = settings->get_local_game_id().ToUint64();
|
data.m_nGameID = settings->get_local_game_id().ToUint64();
|
||||||
data.m_eResult = k_EResultOK;
|
data.m_eResult = k_EResultOK;
|
||||||
|
|
|
@ -0,0 +1,242 @@
|
||||||
|
#include <iostream>
|
||||||
|
#include <string>
|
||||||
|
#include <sstream>
|
||||||
|
#include <vector>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <fstream>
|
||||||
|
#include <iomanip>
|
||||||
|
|
||||||
|
#include <curl/curl.h>
|
||||||
|
#include <json/json.hpp>
|
||||||
|
|
||||||
|
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=<key>&appid=<appid>
|
||||||
|
/*
|
||||||
|
{
|
||||||
|
"game" : {
|
||||||
|
"gameName" : "<name>",
|
||||||
|
"availableGameStats" : {
|
||||||
|
"achievements" : {
|
||||||
|
("<id>" : {
|
||||||
|
"name" : "achievement_name",
|
||||||
|
"displayName" : "achievement name on screen",
|
||||||
|
"hidden" : (0|1),
|
||||||
|
["description" : "<desc>",]
|
||||||
|
"icon" : "<url to icon when achievement is earned>",
|
||||||
|
"icongray" : "<url to icon when achievement is not earned>"
|
||||||
|
},
|
||||||
|
...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
// Get appid infos: http://store.steampowered.com/api/appdetails/?appids=218620
|
||||||
|
/*
|
||||||
|
"appid" : {
|
||||||
|
"success" : (true|false),
|
||||||
|
(success == true "data" : {
|
||||||
|
...
|
||||||
|
"name" : "<name>",
|
||||||
|
"steam_appid" : <appid>,
|
||||||
|
(OPT "dlc" : [<dlc id>, <dlc id>]),
|
||||||
|
"header_image" : "<miniature url>" <-- Use this in the overlay ?
|
||||||
|
(OPT "achievements" : {
|
||||||
|
"total" : <num of achievements>
|
||||||
|
}),
|
||||||
|
"background" : "<background url>" <-- Use this as the overlay background ?
|
||||||
|
(OPT "packages" : [<package id>, <package id>])
|
||||||
|
})
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifdef max
|
||||||
|
#undef max
|
||||||
|
#endif
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
CurlGlobal& cglobal = CurlGlobal::Inst();
|
||||||
|
cglobal.init();
|
||||||
|
|
||||||
|
CurlEasy easy;
|
||||||
|
if (easy.init())
|
||||||
|
{
|
||||||
|
std::string url;
|
||||||
|
std::string steam_apikey;
|
||||||
|
std::string app_id;
|
||||||
|
|
||||||
|
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<std::streamsize>::max(), '\n');
|
||||||
|
std::cin >> steam_apikey;
|
||||||
|
|
||||||
|
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"] = item.value()["hidden"];
|
||||||
|
try
|
||||||
|
{
|
||||||
|
output_json[i]["description"] = item.value()["description"];
|
||||||
|
}
|
||||||
|
catch (...)
|
||||||
|
{
|
||||||
|
output_json[i]["description"] = "";
|
||||||
|
}
|
||||||
|
output_json[i]["icon"] = item.value()["icon"];
|
||||||
|
output_json[i]["icongray"] = item.value()["icongray"];
|
||||||
|
output_json[i]["time_earned"] = 0;
|
||||||
|
output_json[i]["earned"] = 0;
|
||||||
|
++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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue