Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -331,3 +331,24 @@ package_linux.sh <build_folder>
```
`build_folder` is any folder inside `build/linux`, for example: `gmake2/release`
The above example will create a compressed `.tar` archive inside `build/package/linux/`

---

## **EncryptedAppTicket Token Support**

* Add pre-generated encrypted app tickets without modifying code
* Create `configs.user.ini` in your game's settings folder
* Add a line starting with `token=` followed by your Base64-encoded ticket
* Example: `token=CAIxxxxxxxxxxxxxxx...`
* Most valid tickets start with `CAI` after Base64 encoding
* If no token is found, emulator falls back to standard ticket generation

**Why use this?**
* For apps that require specific ticket formats
* When you need to use tickets from real Steam client

To generate a Base64 token from an existing ticket file:
```python
import base64
with open('ticket.bin', 'rb') as f:
print(base64.b64encode(f.read()).decode('utf-8'))
72 changes: 72 additions & 0 deletions dll/base64.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
/* 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/base64.h"

// Base64 character table
static const std::string base64_chars =
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz"
"0123456789+/";

// Check if a character is a valid base64 character
static inline bool is_base64(unsigned char c) {
return (isalnum(c) || (c == '+') || (c == '/'));
}

// Decode a base64 encoded string into a byte vector
std::vector<uint8_t> base64_decode(const std::string& encoded_string) {
int in_len = encoded_string.size();
int i = 0;
int j = 0;
int in_ = 0;
unsigned char char_array_4[4], char_array_3[3];
std::vector<uint8_t> ret;

while (in_len-- && (encoded_string[in_] != '=') && is_base64(encoded_string[in_])) {
char_array_4[i++] = encoded_string[in_]; in_++;
if (i == 4) {
for (i = 0; i < 4; i++)
char_array_4[i] = base64_chars.find(char_array_4[i]);

char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];

for (i = 0; (i < 3); i++)
ret.push_back(char_array_3[i]);
i = 0;
}
}

if (i) {
for (j = i; j < 4; j++)
char_array_4[j] = 0;

for (j = 0; j < 4; j++)
char_array_4[j] = base64_chars.find(char_array_4[j]);

char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];

for (j = 0; (j < i - 1); j++)
ret.push_back(char_array_3[j]);
}

return ret;
}
23 changes: 23 additions & 0 deletions dll/dll/base64.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/* 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/>. */

#pragma once
#include <string>
#include <vector>

// Decodes a base64 encoded string into a byte vector
std::vector<uint8_t> base64_decode(const std::string& encoded_string);
7 changes: 6 additions & 1 deletion dll/dll/settings.h
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,9 @@ class Settings {
constexpr const static int INVALID_IMAGE_HANDLE = 0;
constexpr const static int UNLOADED_IMAGE_HANDLE = -1;

// Base64 encoded token for EncryptedAppTicket
std::string encrypted_app_ticket_token{};

//Depots
std::vector<DepotId_t> depots{};

Expand Down Expand Up @@ -428,6 +431,8 @@ class Settings {
bool hasOverlayAutoAcceptInviteFromFriend(uint64_t friend_id) const;
size_t overlayAutoAcceptInvitesCount() const;

// Method to load token from configs.user.ini
bool load_token_from_config();
};

#endif // SETTINGS_INCLUDE_H
#endif // SETTINGS_INCLUDE_H
31 changes: 31 additions & 0 deletions dll/settings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

#include "dll/settings.h"
#include "dll/steam_app_ids.h"
#include <fstream>


std::string Settings::sanitize(const std::string &name)
Expand Down Expand Up @@ -84,6 +85,7 @@ Settings::Settings(CSteamID steam_id, CGameID game_id, const std::string &name,
this->language = lang;

this->offline = offline;
this->encrypted_app_ticket_token = ""; // Initialize token field
}

// user id
Expand Down Expand Up @@ -417,3 +419,32 @@ size_t Settings::overlayAutoAcceptInvitesCount() const
{
return auto_accept_overlay_invites_friends.size();
}

// Load token from configs.user.ini
bool Settings::load_token_from_config()
{
encrypted_app_ticket_token.clear();

std::string config_path = Local_Storage::get_game_settings_path() + "configs.user.ini";
PRINT_DEBUG("Attempting to read token from %s\n", config_path.c_str());

std::ifstream config_file(utf8_decode(config_path));
if (!config_file.is_open()) {
PRINT_DEBUG("Failed to open configs.user.ini for token reading\n");
return false;
}

std::string line;
while (std::getline(config_file, line)) {
// Look for "token=" at the beginning of line
if (line.find("token=") == 0) {
// Token found - extract everything after "token="
encrypted_app_ticket_token = line.substr(6); // "token=" is 6 characters long
PRINT_DEBUG("Found token in configs.user.ini, length: %zu\n", encrypted_app_ticket_token.length());
return true;
}
}

PRINT_DEBUG("No token found in configs.user.ini\n");
return false;
}
32 changes: 31 additions & 1 deletion dll/steam_user.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
#include "dll/steam_user.h"
#include "dll/auth.h"
#include "dll/appticket.h"
#include "dll/base64.h"

Steam_User::Steam_User(Settings *settings, Local_Storage *local_storage, class Networking *network, class SteamCallResults *callback_results, class SteamCallBacks *callbacks)
{
Expand Down Expand Up @@ -761,6 +762,35 @@ SteamAPICall_t Steam_User::RequestEncryptedAppTicket( void *pDataToInclude, int
bool Steam_User::GetEncryptedAppTicket( void *pTicket, int cbMaxTicket, uint32 *pcbTicket )
{
PRINT_DEBUG("%i %p %p", cbMaxTicket, pTicket, pcbTicket);

// Try to load a token from configs.user.ini first
if (settings->load_token_from_config() && !settings->encrypted_app_ticket_token.empty()) {
PRINT_DEBUG("Using token from configs.user.ini\n");

// Decode token from Base64
std::vector<uint8_t> ticket_data = base64_decode(settings->encrypted_app_ticket_token);

if (ticket_data.empty()) {
PRINT_DEBUG("Failed to decode token from base64\n");
} else {
uint32 ticket_size = static_cast<uint32>(ticket_data.size());
if (pcbTicket) *pcbTicket = ticket_size;

if (cbMaxTicket <= 0) {
if (!pcbTicket) return false;
return true;
}

if (!pTicket) return false;
if (ticket_size > static_cast<uint32>(cbMaxTicket)) return false;

memcpy(pTicket, ticket_data.data(), ticket_size);
PRINT_DEBUG("Successfully used token from configs.user.ini (%u bytes)\n", ticket_size);
return true;
}
}

// Fallback to the standard ticket generation if no token was found or decoded
uint32 ticket_size = static_cast<uint32>(encrypted_app_ticket.size());
if (pcbTicket) *pcbTicket = ticket_size;

Expand Down Expand Up @@ -860,4 +890,4 @@ bool Steam_User::BSetDurationControlOnlineState( EDurationControlOnlineState eNe
{
PRINT_DEBUG_ENTRY();
return false;
}
}