Auto-update offsets in C++
Fetch the current JSON dataset over HTTPS with WinHTTP and parse it when your application starts.
Request
Use JSON for runtime loading. Change the dataset to fflags when you need the flag registry instead of property offsets.
GET
https://api.decamp.dev/v1/latest?game=roblox&dataset=offsets&format=json- game
- roblox
- dataset
- offsets / fflags
- format
- json / hpp
Before you start
01
Link against
winhttp.lib. MSVC can link it from the source file:#pragma comment(lib, "winhttp.lib")02
A JSON parser. This guide uses nlohmann/json.
Fetch the current file
#pragma comment(lib, "winhttp.lib")
#include <windows.h>
#include <winhttp.h>
#include <nlohmann/json.hpp>
#include <cstdint>
#include <iostream>
#include <string>
bool fetch_offsets(std::string& body)
{
body.clear();
auto session = WinHttpOpen(
L"decamp/1.0",
WINHTTP_ACCESS_TYPE_AUTOMATIC_PROXY,
WINHTTP_NO_PROXY_NAME,
WINHTTP_NO_PROXY_BYPASS,
0);
if (!session)
return false;
auto connection = WinHttpConnect(
session,
L"api.decamp.dev",
INTERNET_DEFAULT_HTTPS_PORT,
0);
if (!connection)
{
WinHttpCloseHandle(session);
return false;
}
auto request = WinHttpOpenRequest(
connection,
L"GET",
L"/v1/latest?game=roblox&dataset=offsets&format=json",
nullptr,
WINHTTP_NO_REFERER,
WINHTTP_DEFAULT_ACCEPT_TYPES,
WINHTTP_FLAG_SECURE);
if (!request)
{
WinHttpCloseHandle(connection);
WinHttpCloseHandle(session);
return false;
}
bool success = WinHttpSendRequest(
request,
WINHTTP_NO_ADDITIONAL_HEADERS,
0,
WINHTTP_NO_REQUEST_DATA,
0,
0,
0);
if (success)
success = WinHttpReceiveResponse(request, nullptr);
DWORD status = 0;
DWORD status_size = sizeof(status);
if (success)
{
success = WinHttpQueryHeaders(request,
WINHTTP_QUERY_STATUS_CODE | WINHTTP_QUERY_FLAG_NUMBER,
WINHTTP_HEADER_NAME_BY_INDEX,
&status,
&status_size,
WINHTTP_NO_HEADER_INDEX) && status == 200;
}
while (success)
{
DWORD size = 0;
if (!WinHttpQueryDataAvailable(request, &size))
{
success = false;
break;
}
if (!size)
break;
const auto offset = body.size();
body.resize(offset + size);
DWORD read = 0;
if (!WinHttpReadData(request, body.data() + offset, size, &read))
{
success = false;
break;
}
body.resize(offset + read);
}
WinHttpCloseHandle(request);
WinHttpCloseHandle(connection);
WinHttpCloseHandle(session);
return success;
}Find an offset by name
Read the class and field directly from the nested Offsets object and parse its hexadecimal value.
bool find_offset(
const nlohmann::json& data,
const char* class_name,
const char* property_name,
std::uintptr_t& offset)
{
if (!data.contains("Offsets"))
return false;
const auto& offsets = data["Offsets"];
if (!offsets.contains(class_name))
return false;
const auto& class_offsets = offsets[class_name];
if (!class_offsets.contains(property_name))
return false;
const auto& entry = class_offsets[property_name];
if (!entry.is_string())
return false;
const auto value = entry.get<std::string>();
if (value.rfind("0x", 0) != 0)
return false;
offset = std::stoull(value, nullptr, 16);
return true;
}Putting it together
Fetch once at startup, validate the response, and then read only the values your project needs.
int main()
{
std::string body;
if (!fetch_offsets(body))
{
std::cout << "[-] failed to fetch offsets\n";
return 1;
}
const auto data = nlohmann::json::parse(body, nullptr, false);
if (data.is_discarded())
{
std::cout << "[-] invalid json\n";
return 1;
}
std::uintptr_t balance_speed = 0;
if (!find_offset(data, "ClimbController", "BalanceSpeed", balance_speed))
{
std::cout << "[-] offset is unavailable\n";
return 1;
}
std::cout << "[+] generated: "
<< data.value("Dumped At", std::string{"unknown"}) << '\n';
std::cout << "[+] resolved: "
<< data.value("Total Offsets", 0) << '\n';
std::cout << "[+] ClimbController::BalanceSpeed: 0x"
<< std::hex << balance_speed << '\n';
return 0;
}Responses
200The selected file was returned.304No update. The cached file is still current.400The dataset or format is invalid.404The game or route does not exist.405The endpoint only accepts GET, HEAD, and OPTIONS.409The selected game is known but not available yet.500The service encountered an unexpected error.503The generated file is temporarily unavailable.