this game expects the current app to exist as a owned DLC. if not true, the game will only load the "Return of Rome" game mode, if that DLC is owned, or load the base game with most options disabled if it is not
457 lines
17 KiB
C++
457 lines
17 KiB
C++
/* Copyright (C) 2019 Mr Goldberg
|
|
This file is part of the Goldberg Emulator
|
|
|
|
The Goldberg Emulator is free software; you can redistribute it and/or
|
|
modify it under the terms of the GNU Lesser General Public
|
|
License as published by the Free Software Foundation; either
|
|
version 3 of the License, or (at your option) any later version.
|
|
|
|
The Goldberg Emulator is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
Lesser General Public License for more details.
|
|
|
|
You should have received a copy of the GNU Lesser General Public
|
|
License along with the Goldberg Emulator; if not, see
|
|
<http://www.gnu.org/licenses/>. */
|
|
|
|
#include "dll/steam_apps.h"
|
|
#include "sha/sha1.hpp"
|
|
|
|
Steam_Apps::Steam_Apps(Settings *settings, class SteamCallResults *callback_results)
|
|
{
|
|
this->settings = settings;
|
|
this->callback_results = callback_results;
|
|
}
|
|
|
|
// returns 0 if the key does not exist
|
|
// this may be true on first call, since the app data may not be cached locally yet
|
|
// If you expect it to exists wait for the AppDataChanged_t after the first failure and ask again
|
|
int Steam_Apps::GetAppData( AppId_t nAppID, const char *pchKey, char *pchValue, int cchValueMax )
|
|
{
|
|
//TODO
|
|
PRINT_DEBUG("TODO Steam_Apps::GetAppData %u %s\n", nAppID, pchKey);
|
|
return 0;
|
|
}
|
|
|
|
bool Steam_Apps::BIsSubscribed()
|
|
{
|
|
PRINT_DEBUG("Steam_Apps::BIsSubscribed\n");
|
|
return true;
|
|
}
|
|
|
|
bool Steam_Apps::BIsLowViolence()
|
|
{
|
|
PRINT_DEBUG("Steam_Apps::BIsLowViolence\n");
|
|
return false;
|
|
}
|
|
|
|
bool Steam_Apps::BIsCybercafe()
|
|
{
|
|
PRINT_DEBUG("Steam_Apps::BIsCybercafe\n");
|
|
return false;
|
|
}
|
|
|
|
bool Steam_Apps::BIsVACBanned()
|
|
{
|
|
PRINT_DEBUG("Steam_Apps::BIsVACBanned\n");
|
|
return false;
|
|
}
|
|
|
|
const char *Steam_Apps::GetCurrentGameLanguage()
|
|
{
|
|
PRINT_DEBUG("Steam_Apps::GetCurrentGameLanguage\n");
|
|
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
|
return settings->get_language();
|
|
}
|
|
|
|
const char *Steam_Apps::GetAvailableGameLanguages()
|
|
{
|
|
PRINT_DEBUG("Steam_Apps::GetAvailableGameLanguages\n");
|
|
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
|
//TODO?
|
|
return settings->get_language();
|
|
}
|
|
|
|
|
|
// only use this member if you need to check ownership of another game related to yours, a demo for example
|
|
bool Steam_Apps::BIsSubscribedApp( AppId_t appID )
|
|
{
|
|
PRINT_DEBUG("Steam_Apps::BIsSubscribedApp %u\n", appID);
|
|
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
|
if (appID == 0) return true; //I think appid 0 is always owned
|
|
if (appID == UINT32_MAX) return false; // check Steam_Apps::BIsAppInstalled()
|
|
if (appID == settings->get_local_game_id().AppID()) return true;
|
|
return settings->hasDLC(appID);
|
|
}
|
|
|
|
|
|
// Takes AppID of DLC and checks if the user owns the DLC & if the DLC is installed
|
|
bool Steam_Apps::BIsDlcInstalled( AppId_t appID )
|
|
{
|
|
PRINT_DEBUG("Steam_Apps::BIsDlcInstalled %u\n", appID);
|
|
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
|
if (appID == 0) return true;
|
|
if (appID == UINT32_MAX) return false; // check Steam_Apps::BIsAppInstalled()
|
|
if (appID == settings->get_local_game_id().AppID()) return true; //TODO is this correct?
|
|
return settings->hasDLC(appID);
|
|
}
|
|
|
|
|
|
// returns the Unix time of the purchase of the app
|
|
uint32 Steam_Apps::GetEarliestPurchaseUnixTime( AppId_t nAppID )
|
|
{
|
|
PRINT_DEBUG("Steam_Apps::GetEarliestPurchaseUnixTime\n");
|
|
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
|
if (nAppID == 0) return 0; //TODO is this correct?
|
|
if (nAppID == UINT32_MAX) return 0; // check Steam_Apps::BIsAppInstalled() TODO is this correct?
|
|
if (nAppID == settings->get_local_game_id().AppID() || settings->hasDLC(nAppID)) {
|
|
auto t =
|
|
// 4 days ago
|
|
startup_time
|
|
- std::chrono::hours(24 * 4);
|
|
auto duration = std::chrono::duration_cast<std::chrono::seconds>(t.time_since_epoch());
|
|
return (uint32)duration.count();
|
|
}
|
|
|
|
//TODO ?
|
|
return 0;
|
|
}
|
|
|
|
|
|
// Checks if the user is subscribed to the current app through a free weekend
|
|
// This function will return false for users who have a retail or other type of license
|
|
// Before using, please ask your Valve technical contact how to package and secure your free weekened
|
|
bool Steam_Apps::BIsSubscribedFromFreeWeekend()
|
|
{
|
|
PRINT_DEBUG("Steam_Apps::BIsSubscribedFromFreeWeekend\n");
|
|
return false;
|
|
}
|
|
|
|
|
|
// Returns the number of DLC pieces for the running app
|
|
int Steam_Apps::GetDLCCount()
|
|
{
|
|
PRINT_DEBUG("Steam_Apps::GetDLCCount\n");
|
|
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
|
return settings->DLCCount();
|
|
}
|
|
|
|
|
|
// Returns metadata for DLC by index, of range [0, GetDLCCount()]
|
|
bool Steam_Apps::BGetDLCDataByIndex( int iDLC, AppId_t *pAppID, bool *pbAvailable, char *pchName, int cchNameBufferSize )
|
|
{
|
|
PRINT_DEBUG("Steam_Apps::BGetDLCDataByIndex\n");
|
|
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
|
AppId_t appid = k_uAppIdInvalid;
|
|
bool available = false;
|
|
std::string name{};
|
|
if (!settings->getDLC(iDLC, appid, available, name)) return false;
|
|
|
|
if (pAppID) *pAppID = appid;
|
|
if (pbAvailable) *pbAvailable = available;
|
|
if (pchName && cchNameBufferSize > 0) {
|
|
memset(pchName, 0, cchNameBufferSize);
|
|
name.copy(pchName, cchNameBufferSize - 1);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
// Install/Uninstall control for optional DLC
|
|
void Steam_Apps::InstallDLC( AppId_t nAppID )
|
|
{
|
|
PRINT_DEBUG("Steam_Apps::InstallDLC\n");
|
|
// we lock here because the API is supposed to modify the DLC list
|
|
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
|
}
|
|
|
|
void Steam_Apps::UninstallDLC( AppId_t nAppID )
|
|
{
|
|
PRINT_DEBUG("Steam_Apps::UninstallDLC\n");
|
|
// we lock here because the API is supposed to modify the DLC list
|
|
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
|
}
|
|
|
|
|
|
static void FillProofOfPurchaseKey( AppProofOfPurchaseKeyResponse_t& data, AppId_t nAppID, bool ok_result, std::string key = "cdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd" )
|
|
{
|
|
data.m_nAppID = nAppID;
|
|
if (ok_result) {
|
|
// TODO maybe read this from a config file "purchased_keys.txt":
|
|
// 480=AAAAA-BBBBB-CCCCC-DDDDD
|
|
// 218620=XYZFJ-13370-98765
|
|
size_t min_len = key.size() < k_cubAppProofOfPurchaseKeyMax
|
|
? key.size() < k_cubAppProofOfPurchaseKeyMax
|
|
: k_cubAppProofOfPurchaseKeyMax - 1; // -1 because we need space for null
|
|
data.m_eResult = EResult::k_EResultOK;
|
|
data.m_cchKeyLength = min_len;
|
|
memcpy(data.m_rgchKey, key.c_str(), min_len);
|
|
data.m_rgchKey[min_len] = 0;
|
|
} else {
|
|
data.m_eResult = EResult::k_EResultFail;
|
|
data.m_cchKeyLength = 0;
|
|
data.m_rgchKey[0] = 0;
|
|
data.m_rgchKey[1] = 0;
|
|
}
|
|
}
|
|
|
|
// Request legacy cd-key for yourself or owned DLC. If you are interested in this
|
|
// data then make sure you provide us with a list of valid keys to be distributed
|
|
// to users when they purchase the game, before the game ships.
|
|
// You'll receive an AppProofOfPurchaseKeyResponse_t callback when
|
|
// the key is available (which may be immediately).
|
|
void Steam_Apps::RequestAppProofOfPurchaseKey( AppId_t nAppID )
|
|
{
|
|
PRINT_DEBUG("TODO Steam_Apps::RequestAppProofOfPurchaseKey\n");
|
|
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
|
|
|
AppProofOfPurchaseKeyResponse_t data{};
|
|
data.m_nAppID = nAppID;
|
|
|
|
// check Steam_Apps::BIsAppInstalled()
|
|
if (nAppID == 0 || nAppID == UINT32_MAX) {
|
|
FillProofOfPurchaseKey(data, nAppID, false);
|
|
} else if (nAppID == settings->get_local_game_id().AppID() || settings->hasDLC(nAppID)) {
|
|
FillProofOfPurchaseKey(data, nAppID, true);
|
|
} else {
|
|
//TODO what to do here?
|
|
FillProofOfPurchaseKey(data, nAppID, false);
|
|
}
|
|
|
|
callback_results->addCallResult(data.k_iCallback, &data, sizeof(data));
|
|
}
|
|
|
|
// returns current beta branch name, 'public' is the default branch
|
|
// "true if the user is on a beta branch; otherwise, false"
|
|
// https://partner.steamgames.com/doc/api/ISteamApps
|
|
bool Steam_Apps::GetCurrentBetaName( char *pchName, int cchNameBufferSize )
|
|
{
|
|
PRINT_DEBUG("Steam_Apps::GetCurrentBetaName %i\n", cchNameBufferSize);
|
|
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
|
if (pchName && cchNameBufferSize > settings->current_branch_name.size()) {
|
|
memcpy(pchName, settings->current_branch_name.c_str(), settings->current_branch_name.size());
|
|
}
|
|
|
|
return settings->is_beta_branch;
|
|
}
|
|
|
|
// signal Steam that game files seems corrupt or missing
|
|
bool Steam_Apps::MarkContentCorrupt( bool bMissingFilesOnly )
|
|
{
|
|
PRINT_DEBUG("TODO Steam_Apps::MarkContentCorrupt\n");
|
|
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
|
//TODO: warn user
|
|
return true;
|
|
}
|
|
|
|
// return installed depots in mount order
|
|
uint32 Steam_Apps::GetInstalledDepots( AppId_t appID, DepotId_t *pvecDepots, uint32 cMaxDepots )
|
|
{
|
|
PRINT_DEBUG("Steam_Apps::GetInstalledDepots %u, %u\n", appID, cMaxDepots);
|
|
//TODO not sure about the behavior of this function, I didn't actually test this.
|
|
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
|
unsigned int count = (unsigned int)settings->depots.size();
|
|
if (!pvecDepots || !cMaxDepots || !count) return 0;
|
|
|
|
if (cMaxDepots < count) count = cMaxDepots;
|
|
std::copy(settings->depots.begin(), settings->depots.begin() + count, pvecDepots);
|
|
return count;
|
|
}
|
|
|
|
uint32 Steam_Apps::GetInstalledDepots( DepotId_t *pvecDepots, uint32 cMaxDepots )
|
|
{
|
|
PRINT_DEBUG("Steam_Apps::GetInstalledDepots old\n");
|
|
return GetInstalledDepots( settings->get_local_game_id().AppID(), pvecDepots, cMaxDepots );
|
|
}
|
|
|
|
// returns current app install folder for AppID, returns folder name length
|
|
uint32 Steam_Apps::GetAppInstallDir( AppId_t appID, char *pchFolder, uint32 cchFolderBufferSize )
|
|
{
|
|
PRINT_DEBUG("Steam_Apps::GetAppInstallDir %u %p %u\n", appID, pchFolder, cchFolderBufferSize);
|
|
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
|
//TODO return real path instead of dll path
|
|
std::string installed_path = settings->getAppInstallPath(appID);
|
|
|
|
if (installed_path.empty()) {
|
|
std::string dll_path = get_full_program_path();
|
|
std::string current_path = get_current_path();
|
|
PRINT_DEBUG(" Steam_Apps::GetAppInstallDircurrent dll: '%s', current: '%s'\n", dll_path.c_str(), current_path.c_str());
|
|
|
|
//Just pick the smallest path, it has the most chances of being the good one
|
|
if (dll_path.size() > current_path.size() && current_path.size()) {
|
|
installed_path = current_path;
|
|
} else {
|
|
installed_path = dll_path;
|
|
}
|
|
}
|
|
|
|
PRINT_DEBUG(" Steam_Apps::GetAppInstallDir final path '%s'\n", installed_path.c_str());
|
|
if (pchFolder && cchFolderBufferSize) {
|
|
memset(pchFolder, 0, cchFolderBufferSize);
|
|
installed_path.copy(pchFolder, cchFolderBufferSize - 1);
|
|
}
|
|
|
|
return installed_path.length(); //Real steam always returns the actual path length, not the copied one.
|
|
}
|
|
|
|
// returns true if that app is installed (not necessarily owned)
|
|
// "This only works for base applications, not Downloadable Content (DLC). Use BIsDlcInstalled for DLC instead"
|
|
// https://partner.steamgames.com/doc/api/ISteamApps
|
|
bool Steam_Apps::BIsAppInstalled( AppId_t appID )
|
|
{
|
|
PRINT_DEBUG("Steam_Apps::BIsAppInstalled %u\n", appID);
|
|
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
|
|
|
// "0 Base Goldsource Shared Binaries"
|
|
// https://developer.valvesoftware.com/wiki/Steam_Application_IDs
|
|
if (appID == 0) return true;
|
|
// game LEGO 2K Drive (app id 1451810) checks for a proper steam behavior by sending uint32_max and expects false in return
|
|
if (appID == UINT32_MAX) return false;
|
|
if (appID == settings->get_local_game_id().AppID()) return true;
|
|
|
|
// only check for DLC if the the list is limited/bounded,
|
|
// calling hasDLC() when unlockAllDLCs is true will always satisfy the below condition
|
|
if (!settings->allDLCUnlocked() && settings->hasDLC(appID)) {
|
|
return false;
|
|
}
|
|
|
|
return settings->appIsInstalled(appID);
|
|
}
|
|
|
|
// returns the SteamID of the original owner. If different from current user, it's borrowed
|
|
CSteamID Steam_Apps::GetAppOwner()
|
|
{
|
|
PRINT_DEBUG("Steam_Apps::GetAppOwner\n");
|
|
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
|
return settings->get_local_steam_id();
|
|
}
|
|
|
|
// Returns the associated launch param if the game is run via steam://run/<appid>//?param1=value1;param2=value2;param3=value3 etc.
|
|
// Parameter names starting with the character '@' are reserved for internal use and will always return and empty string.
|
|
// Parameter names starting with an underscore '_' are reserved for steam features -- they can be queried by the game,
|
|
// but it is advised that you not param names beginning with an underscore for your own features.
|
|
const char *Steam_Apps::GetLaunchQueryParam( const char *pchKey )
|
|
{
|
|
PRINT_DEBUG("TODO Steam_Apps::GetLaunchQueryParam\n");
|
|
return "";
|
|
}
|
|
|
|
|
|
// get download progress for optional DLC
|
|
bool Steam_Apps::GetDlcDownloadProgress( AppId_t nAppID, uint64 *punBytesDownloaded, uint64 *punBytesTotal )
|
|
{
|
|
PRINT_DEBUG("Steam_Apps::GetDlcDownloadProgress\n");
|
|
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
|
return false;
|
|
}
|
|
|
|
|
|
// return the buildid of this app, may change at any time based on backend updates to the game
|
|
int Steam_Apps::GetAppBuildId()
|
|
{
|
|
PRINT_DEBUG("Steam_Apps::GetAppBuildId\n");
|
|
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
|
return this->settings->build_id;
|
|
}
|
|
|
|
|
|
// Request all proof of purchase keys for the calling appid and asociated DLC.
|
|
// A series of AppProofOfPurchaseKeyResponse_t callbacks will be sent with
|
|
// appropriate appid values, ending with a final callback where the m_nAppId
|
|
// member is k_uAppIdInvalid (zero).
|
|
void Steam_Apps::RequestAllProofOfPurchaseKeys()
|
|
{
|
|
PRINT_DEBUG("TODO Steam_Apps::RequestAllProofOfPurchaseKeys\n");
|
|
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
|
// current app
|
|
{
|
|
AppProofOfPurchaseKeyResponse_t data{};
|
|
FillProofOfPurchaseKey(data, settings->get_local_game_id().AppID(), true);
|
|
callback_results->addCallResult(data.k_iCallback, &data, sizeof(data));
|
|
}
|
|
|
|
// DLCs
|
|
const auto count = settings->DLCCount();
|
|
for (size_t i = 0; i < settings->DLCCount(); i++) {
|
|
AppId_t app_id;
|
|
bool available;
|
|
std::string name;
|
|
settings->getDLC(i, app_id, available, name);
|
|
|
|
AppProofOfPurchaseKeyResponse_t data{};
|
|
FillProofOfPurchaseKey(data, app_id, true);
|
|
callback_results->addCallResult(data.k_iCallback, &data, sizeof(data));
|
|
}
|
|
|
|
// termination entry
|
|
{
|
|
AppProofOfPurchaseKeyResponse_t data{};
|
|
FillProofOfPurchaseKey(data, k_uAppIdInvalid, true, "");
|
|
callback_results->addCallResult(data.k_iCallback, &data, sizeof(data));
|
|
}
|
|
|
|
}
|
|
|
|
|
|
STEAM_CALL_RESULT( FileDetailsResult_t )
|
|
SteamAPICall_t Steam_Apps::GetFileDetails( const char* pszFileName )
|
|
{
|
|
PRINT_DEBUG("Steam_Apps::GetFileDetails %s\n", pszFileName);
|
|
FileDetailsResult_t data = {};
|
|
//TODO? this function should only return found if file is actually part of the steam depots
|
|
if (file_exists_(pszFileName)) {
|
|
data.m_eResult = k_EResultOK; //
|
|
std::ifstream stream(utf8_decode(pszFileName), std::ios::binary);
|
|
SHA1 checksum;
|
|
checksum.update(stream);
|
|
checksum.final().copy((char *)data.m_FileSHA, sizeof(data.m_FileSHA));
|
|
data.m_ulFileSize = file_size_(pszFileName);
|
|
//TODO data.m_unFlags; 0 is file //TODO: check if 64 is folder
|
|
} else {
|
|
data.m_eResult = k_EResultFileNotFound;
|
|
}
|
|
|
|
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
|
return callback_results->addCallResult(data.k_iCallback, &data, sizeof(data));
|
|
}
|
|
|
|
// Get command line if game was launched via Steam URL, e.g. steam://run/<appid>//<command line>/.
|
|
// This method of passing a connect string (used when joining via rich presence, accepting an
|
|
// invite, etc) is preferable to passing the connect string on the operating system command
|
|
// line, which is a security risk. In order for rich presence joins to go through this
|
|
// path and not be placed on the OS command line, you must set a value in your app's
|
|
// configuration on Steam. Ask Valve for help with this.
|
|
//
|
|
// If game was already running and launched again, the NewUrlLaunchParameters_t will be fired.
|
|
int Steam_Apps::GetLaunchCommandLine( char *pszCommandLine, int cubCommandLine )
|
|
{
|
|
PRINT_DEBUG("TODO Steam_Apps::GetLaunchCommandLine\n");
|
|
return 0;
|
|
}
|
|
|
|
// Check if user borrowed this game via Family Sharing, If true, call GetAppOwner() to get the lender SteamID
|
|
bool Steam_Apps::BIsSubscribedFromFamilySharing()
|
|
{
|
|
PRINT_DEBUG("Steam_Apps::BIsSubscribedFromFamilySharing\n");
|
|
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
|
return false;
|
|
}
|
|
|
|
// check if game is a timed trial with limited playtime
|
|
bool Steam_Apps::BIsTimedTrial( uint32* punSecondsAllowed, uint32* punSecondsPlayed )
|
|
{
|
|
PRINT_DEBUG("Steam_Apps::BIsTimedTrial\n");
|
|
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
|
return false;
|
|
}
|
|
|
|
// set current DLC AppID being played (or 0 if none). Allows Steam to track usage of major DLC extensions
|
|
bool Steam_Apps::SetDlcContext( AppId_t nAppID )
|
|
{
|
|
PRINT_DEBUG("Steam_Apps::SetDlcContext %u\n", nAppID);
|
|
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
|
return true;
|
|
}
|