Initial Release

theres some bugs related to crashes, idk what causes them....
This commit is contained in:
ApfelTeeSaft
2026-03-11 21:40:17 +01:00
parent 58d48a798c
commit 446e6a5b7f
15803 changed files with 4282024 additions and 0 deletions
+401
View File
@@ -0,0 +1,401 @@
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
##
## Get latest from https://github.com/github/gitignore/blob/main/VisualStudio.gitignore
# User-specific files
*.rsuser
*.suo
*.user
*.userosscache
*.sln.docstates
# User-specific files (MonoDevelop/Xamarin Studio)
*.userprefs
# Mono auto generated files
mono_crash.*
# Build results
[Dd]ebug/
[Dd]ebugPublic/
[Rr]elease/
[Rr]eleases/
x64/
x86/
[Ww][Ii][Nn]32/
[Aa][Rr][Mm]/
[Aa][Rr][Mm]64/
bld/
[Bb]in/
[Oo]bj/
[Ll]og/
[Ll]ogs/
# Visual Studio 2015/2017 cache/options directory
.vs/
# Uncomment if you have tasks that create the project's static files in wwwroot
#wwwroot/
# Visual Studio 2017 auto generated files
Generated\ Files/
# MSTest test Results
[Tt]est[Rr]esult*/
[Bb]uild[Ll]og.*
# NUnit
*.VisualState.xml
TestResult.xml
nunit-*.xml
# Build Results of an ATL Project
[Dd]ebugPS/
[Rr]eleasePS/
dlldata.c
# Benchmark Results
BenchmarkDotNet.Artifacts/
# .NET Core
project.lock.json
project.fragment.lock.json
artifacts/
# ASP.NET Scaffolding
ScaffoldingReadMe.txt
# StyleCop
StyleCopReport.xml
# Files built by Visual Studio
*_i.c
*_p.c
*_h.h
*.ilk
*.meta
*.obj
*.iobj
*.pch
*.pdb
*.ipdb
*.pgc
*.pgd
*.rsp
# but not Directory.Build.rsp, as it configures directory-level build defaults
!Directory.Build.rsp
*.sbr
*.tlb
*.tli
*.tlh
*.tmp
*.tmp_proj
*_wpftmp.csproj
*.log
*.tlog
*.vspscc
*.vssscc
.builds
*.pidb
*.svclog
*.scc
# Chutzpah Test files
_Chutzpah*
# Visual C++ cache files
ipch/
*.aps
*.ncb
*.opendb
*.opensdf
*.sdf
*.cachefile
*.VC.db
*.VC.VC.opendb
# Visual Studio profiler
*.psess
*.vsp
*.vspx
*.sap
# Visual Studio Trace Files
*.e2e
# TFS 2012 Local Workspace
$tf/
# Guidance Automation Toolkit
*.gpState
# ReSharper is a .NET coding add-in
_ReSharper*/
*.[Rr]e[Ss]harper
*.DotSettings.user
# TeamCity is a build add-in
_TeamCity*
# DotCover is a Code Coverage Tool
*.dotCover
# AxoCover is a Code Coverage Tool
.axoCover/*
!.axoCover/settings.json
# Coverlet is a free, cross platform Code Coverage Tool
coverage*.json
coverage*.xml
coverage*.info
# Visual Studio code coverage results
*.coverage
*.coveragexml
# NCrunch
_NCrunch_*
.NCrunch_*
.*crunch*.local.xml
nCrunchTemp_*
# MightyMoose
*.mm.*
AutoTest.Net/
# Web workbench (sass)
.sass-cache/
# Installshield output folder
[Ee]xpress/
# DocProject is a documentation generator add-in
DocProject/buildhelp/
DocProject/Help/*.HxT
DocProject/Help/*.HxC
DocProject/Help/*.hhc
DocProject/Help/*.hhk
DocProject/Help/*.hhp
DocProject/Help/Html2
DocProject/Help/html
# Click-Once directory
publish/
# Publish Web Output
*.[Pp]ublish.xml
*.azurePubxml
# Note: Comment the next line if you want to checkin your web deploy settings,
# but database connection strings (with potential passwords) will be unencrypted
*.pubxml
*.publishproj
# Microsoft Azure Web App publish settings. Comment the next line if you want to
# checkin your Azure Web App publish settings, but sensitive information contained
# in these scripts will be unencrypted
PublishScripts/
# NuGet Packages
*.nupkg
# NuGet Symbol Packages
*.snupkg
# The packages folder can be ignored because of Package Restore
**/[Pp]ackages/*
# except build/, which is used as an MSBuild target.
!**/[Pp]ackages/build/
# Uncomment if necessary however generally it will be regenerated when needed
#!**/[Pp]ackages/repositories.config
# NuGet v3's project.json files produces more ignorable files
*.nuget.props
*.nuget.targets
# Microsoft Azure Build Output
csx/
*.build.csdef
# Microsoft Azure Emulator
ecf/
rcf/
# Windows Store app package directories and files
AppPackages/
BundleArtifacts/
Package.StoreAssociation.xml
_pkginfo.txt
*.appx
*.appxbundle
*.appxupload
# Visual Studio cache files
# files ending in .cache can be ignored
*.[Cc]ache
# but keep track of directories ending in .cache
!?*.[Cc]ache/
# Others
ClientBin/
~$*
*~
*.dbmdl
*.dbproj.schemaview
*.jfm
*.pfx
*.publishsettings
orleans.codegen.cs
# Including strong name files can present a security risk
# (https://github.com/github/gitignore/pull/2483#issue-259490424)
#*.snk
# Since there are multiple workflows, uncomment next line to ignore bower_components
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
#bower_components/
# RIA/Silverlight projects
Generated_Code/
# Backup & report files from converting an old project file
# to a newer Visual Studio version. Backup files are not needed,
# because we have git ;-)
_UpgradeReport_Files/
Backup*/
UpgradeLog*.XML
UpgradeLog*.htm
ServiceFabricBackup/
*.rptproj.bak
# SQL Server files
*.mdf
*.ldf
*.ndf
# Business Intelligence projects
*.rdl.data
*.bim.layout
*.bim_*.settings
*.rptproj.rsuser
*- [Bb]ackup.rdl
*- [Bb]ackup ([0-9]).rdl
*- [Bb]ackup ([0-9][0-9]).rdl
# Microsoft Fakes
FakesAssemblies/
# GhostDoc plugin setting file
*.GhostDoc.xml
# Node.js Tools for Visual Studio
.ntvs_analysis.dat
node_modules/
# Visual Studio 6 build log
*.plg
# Visual Studio 6 workspace options file
*.opt
# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
*.vbw
# Visual Studio 6 auto-generated project file (contains which files were open etc.)
*.vbp
# Visual Studio 6 workspace and project file (working project files containing files to include in project)
*.dsw
*.dsp
# Visual Studio 6 technical files
*.ncb
*.aps
# Visual Studio LightSwitch build output
**/*.HTMLClient/GeneratedArtifacts
**/*.DesktopClient/GeneratedArtifacts
**/*.DesktopClient/ModelManifest.xml
**/*.Server/GeneratedArtifacts
**/*.Server/ModelManifest.xml
_Pvt_Extensions
# Paket dependency manager
.paket/paket.exe
paket-files/
# FAKE - F# Make
.fake/
# CodeRush personal settings
.cr/personal
# Python Tools for Visual Studio (PTVS)
__pycache__/
*.pyc
# Cake - Uncomment if you are using it
# tools/**
# !tools/packages.config
# Tabs Studio
*.tss
# Telerik's JustMock configuration file
*.jmconfig
# BizTalk build output
*.btp.cs
*.btm.cs
*.odx.cs
*.xsd.cs
# OpenCover UI analysis results
OpenCover/
# Azure Stream Analytics local run output
ASALocalRun/
# MSBuild Binary and Structured Log
*.binlog
# NVidia Nsight GPU debugger configuration file
*.nvuser
# MFractors (Xamarin productivity tool) working folder
.mfractor/
# Local History for Visual Studio
.localhistory/
# Visual Studio History (VSHistory) files
.vshistory/
# BeatPulse healthcheck temp database
healthchecksdb
# Backup folder for Package Reference Convert tool in Visual Studio 2017
MigrationBackup/
# Ionide (cross platform F# VS Code tools) working folder
.ionide/
# Fody - auto-generated XML schema
FodyWeavers.xsd
# VS Code files for those working on multiple tools
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
*.code-workspace
# Local History for Visual Studio Code
.history/
# Windows Installer files from build outputs
*.cab
*.msi
*.msix
*.msm
*.msp
# JetBrains Rider
*.sln.iml
+22
View File
@@ -0,0 +1,22 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.0.31903.59
MinimumVisualStudioVersion = 10.0.40219.1
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Embeddium", "Embeddium\Embeddium.vcxproj", "{A1B2C3D4-E5F6-7890-ABCD-EF1234567890}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|x64 = Debug|x64
Release|x64 = Release|x64
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{A1B2C3D4-E5F6-7890-ABCD-EF1234567890}.Debug|x64.ActiveCfg = Debug|x64
{A1B2C3D4-E5F6-7890-ABCD-EF1234567890}.Debug|x64.Build.0 = Debug|x64
{A1B2C3D4-E5F6-7890-ABCD-EF1234567890}.Release|x64.ActiveCfg = Release|x64
{A1B2C3D4-E5F6-7890-ABCD-EF1234567890}.Release|x64.Build.0 = Release|x64
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
EndGlobal
+157
View File
@@ -0,0 +1,157 @@
#include "AssetManager.h"
#include "EmbeddedAssets.h"
namespace Embeddium {
// ─────────────────────────────────────────────────────────────────────────────
// Static storage
// ─────────────────────────────────────────────────────────────────────────────
std::unordered_map<std::string, std::string_view> AssetManager::sTextAssets;
std::unordered_map<std::string, std::span<const uint8_t>> AssetManager::sBinaryAssets;
void AssetManager::RegisterText(std::string name, const char* data, size_t size) {
sTextAssets.emplace(std::move(name), std::string_view(data, size));
}
void AssetManager::RegisterBinary(std::string name, const uint8_t* data, size_t size) {
sBinaryAssets.emplace(std::move(name), std::span<const uint8_t>(data, size));
}
// ─────────────────────────────────────────────────────────────────────────────
// Init — registers all embedded assets from EmbeddedAssets.cpp.
// Must be called exactly once before any Get* call.
// ─────────────────────────────────────────────────────────────────────────────
void AssetManager::Init() {
using namespace EmbeddedAssets;
// ── Config ──────────────────────────────────────────────────────────────
RegisterText("config.ini", kConfigIni, kConfigIniSize);
RegisterText("catalog_config.json", kCatalogConfigJson, kCatalogConfigJsonSize);
// ── Responses: top-level ────────────────────────────────────────────────
RegisterText("catalog.json", kCatalogJson, kCatalogJsonSize);
RegisterText("keychain.json", kKeychainJson, kKeychainJsonSize);
RegisterText("SAC.json", kSACJson, kSACJsonSize);
RegisterText("privacy.json", kPrivacyJson, kPrivacyJsonSize);
RegisterText("friendslist.json", kFriendslistJson, kFriendslistJsonSize);
RegisterText("friendslist2.json", kFriendslist2Json, kFriendslist2JsonSize);
RegisterText("epic-settings.json", kEpicSettingsJson, kEpicSettingsJsonSize);
RegisterText("contentpages.json", kContentPagesJson, kContentPagesJsonSize);
RegisterText("sdkv1.json", kSdkV1Json, kSdkV1JsonSize);
// ── Responses: Athena ───────────────────────────────────────────────────
RegisterText("Athena/motd.json", kAthenaMOTDJson, kAthenaMOTDJsonSize);
RegisterText("Athena/quests.json", kAthenaQuestsJson, kAthenaQuestsJsonSize);
RegisterText("Athena/SeasonData.json", kAthenaSeasonDataJson, kAthenaSeasonDataJsonSize);
RegisterText("Athena/seasonPasses.json", kAthenaSeasonPassesJson, kAthenaSeasonPassesJsonSize);
RegisterText("Athena/sparkTracks.json", kAthenaSparkTracksJson, kAthenaSparkTracksJsonSize);
RegisterText("Athena/winterfestRewards.json",kAthenaWinterfestJson, kAthenaWinterfestJsonSize);
RegisterText("Athena/BattlePass/Season2.json", kBPSeason2Json, kBPSeason2JsonSize);
RegisterText("Athena/BattlePass/Season3.json", kBPSeason3Json, kBPSeason3JsonSize);
RegisterText("Athena/BattlePass/Season4.json", kBPSeason4Json, kBPSeason4JsonSize);
RegisterText("Athena/BattlePass/Season5.json", kBPSeason5Json, kBPSeason5JsonSize);
RegisterText("Athena/BattlePass/Season6.json", kBPSeason6Json, kBPSeason6JsonSize);
RegisterText("Athena/BattlePass/Season7.json", kBPSeason7Json, kBPSeason7JsonSize);
RegisterText("Athena/BattlePass/Season8.json", kBPSeason8Json, kBPSeason8JsonSize);
RegisterText("Athena/BattlePass/Season9.json", kBPSeason9Json, kBPSeason9JsonSize);
RegisterText("Athena/BattlePass/Season10.json", kBPSeason10Json, kBPSeason10JsonSize);
RegisterText("Athena/Discovery/discovery_api_assets.json",
kDiscoveryApiAssetsJson, kDiscoveryApiAssetsJsonSize);
RegisterText("Athena/Discovery/discovery_frontend.json",
kDiscoveryFrontendJson, kDiscoveryFrontendJsonSize);
RegisterText("Athena/Tournament/history.json",
kTournamentHistoryJson, kTournamentHistoryJsonSize);
RegisterText("Athena/Tournament/tournament.json",
kTournamentJson, kTournamentJsonSize);
RegisterText("Athena/Tournament/leaderboard.json",
kTournamentLeaderboardJson, kTournamentLeaderboardJsonSize);
RegisterText("Athena/Tournament/tournamentandhistory.json",
kTournamentAndHistoryJson, kTournamentAndHistoryJsonSize);
// ── Responses: Campaign ─────────────────────────────────────────────────
RegisterText("Campaign/cardPackData.json", kCampaignCardPackDataJson, kCampaignCardPackDataJsonSize);
RegisterText("Campaign/heroNames.json", kCampaignHeroNamesJson, kCampaignHeroNamesJsonSize);
RegisterText("Campaign/quests.json", kCampaignQuestsJson, kCampaignQuestsJsonSize);
RegisterText("Campaign/expeditionData.json", kCampaignExpeditionDataJson,kCampaignExpeditionDataJsonSize);
RegisterText("Campaign/rewards.json", kCampaignRewardsJson, kCampaignRewardsJsonSize);
RegisterText("Campaign/transformItemIDS.json",kCampaignTransformItemJson, kCampaignTransformItemJsonSize);
RegisterText("Campaign/survivorData.json", kCampaignSurvivorDataJson, kCampaignSurvivorDataJsonSize);
RegisterText("Campaign/dailyRewards.json", kCampaignDailyRewardsJson, kCampaignDailyRewardsJsonSize);
RegisterText("Campaign/worldstw.json", kCampaignWorldSTWJson, kCampaignWorldSTWJsonSize);
// ── Profiles (default state) ─────────────────────────────────────────────
RegisterText("profiles/athena.json",
kProfileAthenaJson, kProfileAthenaJsonSize);
RegisterText("profiles/common_core.json",
kProfileCommonCoreJson, kProfileCommonCoreJsonSize);
RegisterText("profiles/common_public.json",
kProfileCommonPublicJson, kProfileCommonPublicJsonSize);
RegisterText("profiles/campaign.json",
kProfileCampaignJson, kProfileCampaignJsonSize);
RegisterText("profiles/profile0.json",
kProfileProfile0Json, kProfileProfile0JsonSize);
RegisterText("profiles/collection_book_people0.json",
kProfileCollectionBookPeople0Json, kProfileCollectionBookPeople0JsonSize);
RegisterText("profiles/collection_book_schematics0.json",
kProfileCollectionBookSchematics0Json, kProfileCollectionBookSchematics0JsonSize);
RegisterText("profiles/collections.json",
kProfileCollectionsJson, kProfileCollectionsJsonSize);
RegisterText("profiles/creative.json",
kProfileCreativeJson, kProfileCreativeJsonSize);
RegisterText("profiles/metadata.json",
kProfileMetadataJson, kProfileMetadataJsonSize);
RegisterText("profiles/outpost0.json",
kProfileOutpost0Json, kProfileOutpost0JsonSize);
RegisterText("profiles/theater0.json",
kProfileTheater0Json, kProfileTheater0JsonSize);
// ── CloudStorage INI files ───────────────────────────────────────────────
RegisterText("CloudStorage/DefaultEngine.ini",
kCloudStorageDefaultEngineIni, kCloudStorageDefaultEngineIniSize);
RegisterText("CloudStorage/DefaultGame.ini",
kCloudStorageDefaultGameIni, kCloudStorageDefaultGameIniSize);
RegisterText("CloudStorage/DefaultInput.ini",
kCloudStorageDefaultInputIni, kCloudStorageDefaultInputIniSize);
RegisterText("CloudStorage/DefaultRuntimeOptions.ini",
kCloudStorageDefaultRuntimeOptionsIni,
kCloudStorageDefaultRuntimeOptionsIniSize);
// ── CloudDir binary / text assets ───────────────────────────────────────
RegisterBinary("CloudDir/embeddiumServer.manifest",
kCloudDirManifest, kCloudDirManifestSize);
RegisterBinary("CloudDir/embeddiumServer.chunk",
kCloudDirChunk, kCloudDirChunkSize);
RegisterText("CloudDir/Full.ini",
kCloudDirFullIni, kCloudDirFullIniSize);
}
// ─────────────────────────────────────────────────────────────────────────────
// Accessors
// ─────────────────────────────────────────────────────────────────────────────
std::string_view AssetManager::GetJSON(std::string_view name) {
auto it = sTextAssets.find(std::string(name));
if (it == sTextAssets.end()) return {};
return it->second;
}
std::string_view AssetManager::GetText(std::string_view name) {
auto it = sTextAssets.find(std::string(name));
if (it == sTextAssets.end()) return {};
return it->second;
}
std::span<const uint8_t> AssetManager::GetBinary(std::string_view name) {
auto it = sBinaryAssets.find(std::string(name));
if (it == sBinaryAssets.end()) return {};
return it->second;
}
bool AssetManager::Has(std::string_view name) {
std::string key(name);
return sTextAssets.count(key) || sBinaryAssets.count(key);
}
} // namespace Embeddium
+54
View File
@@ -0,0 +1,54 @@
#pragma once
#include <string>
#include <string_view>
#include <span>
#include <cstdint>
#include <unordered_map>
namespace Embeddium {
// ─────────────────────────────────────────────────────────────────────────────
// AssetManager
// Provides runtime lookup of assets embedded directly into the DLL binary.
//
// All assets are registered at startup from EmbeddedAssets.cpp.
// After registration, all access is read-only.
//
// Usage:
// std::string_view json = AssetManager::GetJSON("catalog.json");
// std::string_view text = AssetManager::GetText("config.ini");
// auto bytes = AssetManager::GetBinary("embeddiumServer.manifest");
// ─────────────────────────────────────────────────────────────────────────────
class AssetManager {
public:
// Initialize: register all embedded assets. Called once from Initializer.
// After Init() returns, all Get* calls are thread-safe (read-only).
static void Init();
// Get a JSON asset by filename (e.g. "catalog.json", "Athena/motd.json").
// Returns an empty string_view if the asset is not found.
static std::string_view GetJSON(std::string_view name);
// Get a text asset by filename (e.g. "config.ini", "DefaultEngine.ini").
// Returns an empty string_view if the asset is not found.
static std::string_view GetText(std::string_view name);
// Get a binary asset by filename (e.g. "embeddiumServer.manifest").
// Returns an empty span if the asset is not found.
static std::span<const uint8_t> GetBinary(std::string_view name);
// Returns true if any asset with the given name is registered.
static bool Has(std::string_view name);
private:
// Key: filename (relative path from asset root, e.g. "Athena/motd.json")
// Value: pointer + size into compile-time static storage
static std::unordered_map<std::string, std::string_view> sTextAssets;
static std::unordered_map<std::string,
std::span<const uint8_t>> sBinaryAssets;
static void RegisterText(std::string name, const char* data, size_t size);
static void RegisterBinary(std::string name, const uint8_t* data, size_t size);
};
} // namespace Embeddium
File diff suppressed because it is too large Load Diff
+202
View File
@@ -0,0 +1,202 @@
#pragma once
#include <cstddef>
#include <cstdint>
// ─────────────────────────────────────────────────────────────────────────────
// EmbeddedAssets
// Declarations for all assets compiled directly into the DLL binary.
// Definitions are in EmbeddedAssets.cpp (auto-generated from source files).
//
// Binary assets (manifest, chunk) are uint8_t arrays.
// All other assets (JSON, INI) are null-terminated char arrays.
// ─────────────────────────────────────────────────────────────────────────────
namespace Embeddium::EmbeddedAssets {
// ── Config ───────────────────────────────────────────────────────────────────
extern const char kConfigIni[];
extern const size_t kConfigIniSize;
extern const char kCatalogConfigJson[];
extern const size_t kCatalogConfigJsonSize;
// ── Responses: top-level ─────────────────────────────────────────────────────
extern const char kCatalogJson[];
extern const size_t kCatalogJsonSize;
extern const char kKeychainJson[];
extern const size_t kKeychainJsonSize;
extern const char kSACJson[];
extern const size_t kSACJsonSize;
extern const char kPrivacyJson[];
extern const size_t kPrivacyJsonSize;
extern const char kFriendslistJson[];
extern const size_t kFriendslistJsonSize;
extern const char kFriendslist2Json[];
extern const size_t kFriendslist2JsonSize;
extern const char kEpicSettingsJson[];
extern const size_t kEpicSettingsJsonSize;
extern const char kContentPagesJson[];
extern const size_t kContentPagesJsonSize;
extern const char kSdkV1Json[];
extern const size_t kSdkV1JsonSize;
// ── Responses: Athena ─────────────────────────────────────────────────────────
extern const char kAthenaMOTDJson[];
extern const size_t kAthenaMOTDJsonSize;
extern const char kAthenaQuestsJson[];
extern const size_t kAthenaQuestsJsonSize;
extern const char kAthenaSeasonDataJson[];
extern const size_t kAthenaSeasonDataJsonSize;
extern const char kAthenaSeasonPassesJson[];
extern const size_t kAthenaSeasonPassesJsonSize;
extern const char kAthenaSparkTracksJson[];
extern const size_t kAthenaSparkTracksJsonSize;
extern const char kAthenaWinterfestJson[];
extern const size_t kAthenaWinterfestJsonSize;
extern const char kBPSeason2Json[];
extern const size_t kBPSeason2JsonSize;
extern const char kBPSeason3Json[];
extern const size_t kBPSeason3JsonSize;
extern const char kBPSeason4Json[];
extern const size_t kBPSeason4JsonSize;
extern const char kBPSeason5Json[];
extern const size_t kBPSeason5JsonSize;
extern const char kBPSeason6Json[];
extern const size_t kBPSeason6JsonSize;
extern const char kBPSeason7Json[];
extern const size_t kBPSeason7JsonSize;
extern const char kBPSeason8Json[];
extern const size_t kBPSeason8JsonSize;
extern const char kBPSeason9Json[];
extern const size_t kBPSeason9JsonSize;
extern const char kBPSeason10Json[];
extern const size_t kBPSeason10JsonSize;
extern const char kDiscoveryApiAssetsJson[];
extern const size_t kDiscoveryApiAssetsJsonSize;
extern const char kDiscoveryFrontendJson[];
extern const size_t kDiscoveryFrontendJsonSize;
extern const char kTournamentHistoryJson[];
extern const size_t kTournamentHistoryJsonSize;
extern const char kTournamentJson[];
extern const size_t kTournamentJsonSize;
extern const char kTournamentLeaderboardJson[];
extern const size_t kTournamentLeaderboardJsonSize;
extern const char kTournamentAndHistoryJson[];
extern const size_t kTournamentAndHistoryJsonSize;
// ── Responses: Campaign ───────────────────────────────────────────────────────
extern const char kCampaignCardPackDataJson[];
extern const size_t kCampaignCardPackDataJsonSize;
extern const char kCampaignHeroNamesJson[];
extern const size_t kCampaignHeroNamesJsonSize;
extern const char kCampaignQuestsJson[];
extern const size_t kCampaignQuestsJsonSize;
extern const char kCampaignExpeditionDataJson[];
extern const size_t kCampaignExpeditionDataJsonSize;
extern const char kCampaignRewardsJson[];
extern const size_t kCampaignRewardsJsonSize;
extern const char kCampaignTransformItemJson[];
extern const size_t kCampaignTransformItemJsonSize;
extern const char kCampaignSurvivorDataJson[];
extern const size_t kCampaignSurvivorDataJsonSize;
extern const char kCampaignDailyRewardsJson[];
extern const size_t kCampaignDailyRewardsJsonSize;
extern const char kCampaignWorldSTWJson[];
extern const size_t kCampaignWorldSTWJsonSize;
// ── Profiles (default initial state) ─────────────────────────────────────────
extern const char kProfileAthenaJson[];
extern const size_t kProfileAthenaJsonSize;
extern const char kProfileCommonCoreJson[];
extern const size_t kProfileCommonCoreJsonSize;
extern const char kProfileCommonPublicJson[];
extern const size_t kProfileCommonPublicJsonSize;
extern const char kProfileCampaignJson[];
extern const size_t kProfileCampaignJsonSize;
extern const char kProfileProfile0Json[];
extern const size_t kProfileProfile0JsonSize;
extern const char kProfileCollectionBookPeople0Json[];
extern const size_t kProfileCollectionBookPeople0JsonSize;
extern const char kProfileCollectionBookSchematics0Json[];
extern const size_t kProfileCollectionBookSchematics0JsonSize;
extern const char kProfileCollectionsJson[];
extern const size_t kProfileCollectionsJsonSize;
extern const char kProfileCreativeJson[];
extern const size_t kProfileCreativeJsonSize;
extern const char kProfileMetadataJson[];
extern const size_t kProfileMetadataJsonSize;
extern const char kProfileOutpost0Json[];
extern const size_t kProfileOutpost0JsonSize;
extern const char kProfileTheater0Json[];
extern const size_t kProfileTheater0JsonSize;
// ── CloudStorage INI files ────────────────────────────────────────────────────
extern const char kCloudStorageDefaultEngineIni[];
extern const size_t kCloudStorageDefaultEngineIniSize;
extern const char kCloudStorageDefaultGameIni[];
extern const size_t kCloudStorageDefaultGameIniSize;
extern const char kCloudStorageDefaultInputIni[];
extern const size_t kCloudStorageDefaultInputIniSize;
extern const char kCloudStorageDefaultRuntimeOptionsIni[];
extern const size_t kCloudStorageDefaultRuntimeOptionsIniSize;
// ── CloudDir binary assets ────────────────────────────────────────────────────
extern const uint8_t kCloudDirManifest[];
extern const size_t kCloudDirManifestSize;
extern const uint8_t kCloudDirChunk[];
extern const size_t kCloudDirChunkSize;
extern const char kCloudDirFullIni[];
extern const size_t kCloudDirFullIniSize;
} // namespace Embeddium::EmbeddedAssets
+41
View File
@@ -0,0 +1,41 @@
#include "AccountState.h"
#include "../Utilities/StringUtils.h"
namespace Embeddium {
std::atomic<std::shared_ptr<std::string>> AccountState::sDisplayName_{
std::make_shared<std::string>("Embeddium")};
std::atomic<bool> AccountState::sUseConfig_{false};
void AccountState::Init(std::string configDisplayName, bool useConfigName) {
// Strip @domain from config name just in case
auto at = configDisplayName.find('@');
if (at != std::string::npos) configDisplayName = configDisplayName.substr(0, at);
sUseConfig_.store(useConfigName, std::memory_order_release);
sDisplayName_.store(
std::make_shared<std::string>(std::move(configDisplayName)),
std::memory_order_release);
}
void AccountState::SetFromLogin(std::string_view raw) {
// If bUseConfigDisplayName is true, ignore the login value
if (sUseConfig_.load(std::memory_order_acquire)) return;
std::string name(raw);
// Strip @domain suffix
auto at = name.find('@');
if (at != std::string::npos) name = name.substr(0, at);
if (name.empty()) name = "Embeddium";
sDisplayName_.store(
std::make_shared<std::string>(std::move(name)),
std::memory_order_release);
}
std::string AccountState::Get() {
auto ptr = sDisplayName_.load(std::memory_order_acquire);
return ptr ? *ptr : "Embeddium";
}
} // namespace Embeddium
+40
View File
@@ -0,0 +1,40 @@
#pragma once
#include <string>
#include <string_view>
#include <atomic>
#include <memory>
namespace Embeddium {
// ─────────────────────────────────────────────────────────────────────────────
// AccountState
// Holds the "current account" display name.
//
// Write model: written exactly once — on the first OAuth token request
// (when the game logs in). After that it is read-only.
//
// Thread safety: uses an atomic shared_ptr so the initial write and all
// subsequent reads are race-free.
// ─────────────────────────────────────────────────────────────────────────────
class AccountState {
public:
// Initialise with the display name from config (may be overridden at login)
static void Init(std::string configDisplayName, bool useConfigName);
// Called at login time — sets the display name from the provided value
// unless bUseConfigDisplayName was true at Init time.
// Strips @domain if present.
static void SetFromLogin(std::string_view raw);
// Returns the current display name (never empty after Init)
static std::string Get();
// Returns a fixed "account ID" for this session (same as display name
static std::string GetAccountId() { return Get(); }
private:
static std::atomic<std::shared_ptr<std::string>> sDisplayName_;
static std::atomic<bool> sUseConfig_;
};
} // namespace Embeddium
+156
View File
@@ -0,0 +1,156 @@
#include "CloudStorageService.h"
#include "../Assets/AssetManager.h"
#include "../Utilities/HashUtils.h"
#include "../Utilities/StringUtils.h"
#include <Windows.h>
#include <fstream>
#include <sstream>
#include <filesystem>
#include <chrono>
#include <iomanip>
namespace Embeddium {
namespace fs = std::filesystem;
static std::string NowIso() {
auto now = std::chrono::system_clock::now();
auto t = std::chrono::system_clock::to_time_t(now);
std::tm tm{};
gmtime_s(&tm, &t);
std::ostringstream ss;
ss << std::put_time(&tm, "%Y-%m-%dT%H:%M:%S.000Z");
return ss.str();
}
// ─────────────────────────────────────────────────────────────────────────────
// GetSystemFiles
// ─────────────────────────────────────────────────────────────────────────────
Json CloudStorageService::GetSystemFiles(const VersionInfo& ver) {
// 9.4010.40 returns 404 (caller must check this condition)
if (ver.build >= 9.40f && ver.build <= 10.40f) return Json::array();
Json arr = Json::array();
std::string now = NowIso();
for (const char* name : kSystemFiles) {
std::string key = std::string("CloudStorage/") + name;
auto text = AssetManager::GetText(key);
if (text.empty()) continue;
arr.push_back({
{"uniqueFilename", name},
{"filename", name},
{"hash", HashUtils::SHA1Hex(text)},
{"hash256", HashUtils::SHA256Hex(text)},
{"length", static_cast<int>(text.size())},
{"contentType", "application/octet-stream"},
{"uploaded", now},
{"storageType", "S3"},
{"storageIds", Json::object()},
{"doNotCache", true}
});
}
return arr;
}
// ─────────────────────────────────────────────────────────────────────────────
// GetSystemFile
// ─────────────────────────────────────────────────────────────────────────────
std::string CloudStorageService::GetSystemFile(std::string_view filename,
const VersionInfo& ver) {
std::string key = "CloudStorage/" + std::string(filename);
auto text = AssetManager::GetText(key);
if (text.empty()) return {};
std::string content(text);
// Fixes kicks on 23+ — disable encryption
if (filename == "DefaultEngine.ini" && ver.season >= 23) {
content += "\n[ConsoleVariables]\nnet.AllowEncryption=0\n";
}
// Hotfix: prevent STW redirect bug on 17.5019.30
if (filename == "DefaultRuntimeOptions.ini" &&
ver.build >= 17.50f && ver.build <= 19.30f) {
content += "\nbLoadDirectlyIntoLobby=false\n";
}
return content;
}
// ─────────────────────────────────────────────────────────────────────────────
// ClientSettings helpers
// ─────────────────────────────────────────────────────────────────────────────
std::string CloudStorageService::GetClientSettingsPath(std::string_view cl) {
// Use %LOCALAPPDATA%\EmbeddiumServer\ClientSettings\ if available,
// else a temp folder.
std::string base;
char* localAppData = nullptr;
size_t sz = 0;
if (_dupenv_s(&localAppData, &sz, "LOCALAPPDATA") == 0 && localAppData) {
base = std::string(localAppData) + "\\EmbeddiumServer\\ClientSettings";
free(localAppData);
} else {
char* tmp = nullptr; size_t tmpSz = 0;
if (_dupenv_s(&tmp, &tmpSz, "TEMP") == 0 && tmp) {
base = std::string(tmp) + "\\EmbeddiumServer\\ClientSettings";
free(tmp);
} else {
base = "C:\\Temp\\EmbeddiumServer\\ClientSettings";
}
}
try { fs::create_directories(base); } catch (...) {}
return base + "\\ClientSettings-" + std::string(cl) + ".Sav";
}
bool CloudStorageService::HasClientSettings(std::string_view cl) {
return fs::exists(GetClientSettingsPath(cl));
}
std::string CloudStorageService::ReadClientSettings(std::string_view cl) {
std::string path = GetClientSettingsPath(cl);
std::ifstream f(path, std::ios::binary);
if (!f) return {};
return {std::istreambuf_iterator<char>(f), std::istreambuf_iterator<char>()};
}
void CloudStorageService::WriteClientSettings(std::string_view cl,
std::string_view data) {
std::string path = GetClientSettingsPath(cl);
std::ofstream f(path, std::ios::binary);
f.write(data.data(), static_cast<std::streamsize>(data.size()));
}
// ─────────────────────────────────────────────────────────────────────────────
// GetUserFileList
// ─────────────────────────────────────────────────────────────────────────────
Json CloudStorageService::GetUserFileList(std::string_view accountId,
std::string_view cl) {
if (!HasClientSettings(cl)) return Json::array();
std::string content = ReadClientSettings(cl);
std::string now = NowIso();
return Json::array({
{
{"uniqueFilename", "ClientSettings.Sav"},
{"filename", "ClientSettings.Sav"},
{"hash", HashUtils::SHA1Hex(content)},
{"hash256", HashUtils::SHA256Hex(content)},
{"length", static_cast<int>(content.size())},
{"contentType", "application/octet-stream"},
{"uploaded", now},
{"storageType", "S3"},
{"storageIds", Json::object()},
{"accountId", std::string(accountId)},
{"doNotCache", true}
}
});
}
} // namespace Embeddium
+56
View File
@@ -0,0 +1,56 @@
#pragma once
#include "../Utilities/JsonUtils.h"
#include "VersionService.h"
#include <string>
#include <string_view>
namespace Embeddium {
// ─────────────────────────────────────────────────────────────────────────────
// CloudStorageService
// Handles /fortnite/api/cloudstorage/system* and
// /fortnite/api/cloudstorage/user/* routes.
//
// System files come from embedded CloudStorage/*.ini assets.
// User ClientSettings are stored in the process's temp directory to allow
// the game to save keybinds and settings between sessions.
// ─────────────────────────────────────────────────────────────────────────────
class CloudStorageService {
public:
// Returns a JSON array of system cloud file descriptors
static Json GetSystemFiles(const VersionInfo& ver);
// Returns the content of a named system file (e.g. "DefaultEngine.ini").
// Applies per-version patches (encryption fix, STW hotfixes).
// Returns empty string_view if not found.
static std::string GetSystemFile(std::string_view filename,
const VersionInfo& ver);
// Returns the ClientSettings file path for the given CL string.
// Creates the directory if needed.
static std::string GetClientSettingsPath(std::string_view cl);
// Returns true if a ClientSettings.Sav file exists for the given CL.
static bool HasClientSettings(std::string_view cl);
// Reads raw bytes from the ClientSettings file. Returns empty on failure.
static std::string ReadClientSettings(std::string_view cl);
// Writes raw bytes to the ClientSettings file.
static void WriteClientSettings(std::string_view cl,
std::string_view data);
// Returns JSON array with a single user file descriptor (ClientSettings).
static Json GetUserFileList(std::string_view accountId,
std::string_view cl);
private:
static constexpr const char* kSystemFiles[] = {
"DefaultEngine.ini",
"DefaultGame.ini",
"DefaultInput.ini",
"DefaultRuntimeOptions.ini"
};
};
} // namespace Embeddium
+139
View File
@@ -0,0 +1,139 @@
#include "ContentService.h"
#include "../Assets/AssetManager.h"
#include "../Utilities/StringUtils.h"
namespace Embeddium {
// ─────────────────────────────────────────────────────────────────────────────
// Mirrors chooseTranslationsInJSON() from functions.js
// ─────────────────────────────────────────────────────────────────────────────
void ContentService::ChooseTranslations(Json& obj, std::string_view lang) {
if (obj.is_array()) {
for (auto& elem : obj) ChooseTranslations(elem, lang);
} else if (obj.is_object()) {
for (auto& [k, v] : obj.items()) {
if (v.is_object()) {
if (v.contains(std::string(lang))) {
v = v[std::string(lang)];
} else if (v.contains("en")) {
v = v["en"];
} else {
ChooseTranslations(v, lang);
}
}
}
}
}
void ContentService::PatchBackgrounds(Json& cp, const VersionInfo& ver) {
float b = ver.build;
int s = ver.season;
try {
auto& bgs = cp["dynamicbackgrounds"]["backgrounds"]["backgrounds"];
std::string season = "season" + std::to_string(s) + (s >= 21 ? "00" : "");
bgs[0]["stage"] = season;
bgs[1]["stage"] = season;
switch (s) {
case 9:
cp["lobby"]["backgroundimage"] = "";
cp["lobby"]["stage"] = "default";
break;
case 10:
bgs[0]["stage"] = "seasonx";
bgs[1]["stage"] = "seasonx";
break;
case 20:
bgs[0]["backgroundimage"] = "https://cdn2.unrealengine.com/t-bp20-lobby-2048x1024-d89eb522746c.png";
break;
case 21:
bgs[0]["backgroundimage"] = "https://cdn2.unrealengine.com/s21-lobby-background-2048x1024-2e7112b25dc3.jpg";
break;
case 22:
bgs[0]["backgroundimage"] = "https://cdn2.unrealengine.com/t-bp22-lobby-square-2048x2048-2048x2048-e4e90c6e8018.jpg";
break;
case 23:
bgs[0]["backgroundimage"] = "https://cdn2.unrealengine.com/t-bp23-lobby-2048x1024-2048x1024-26f2c1b27f63.png";
break;
case 24:
bgs[0]["backgroundimage"] = "https://cdn2.unrealengine.com/t-ch4s2-bp-lobby-4096x2048-edde08d15f7e.jpg";
break;
case 25:
bgs[0]["backgroundimage"] = "https://cdn2.unrealengine.com/s25-lobby-4k-4096x2048-4a832928e11f.jpg";
bgs[1]["backgroundimage"] = "https://cdn2.unrealengine.com/fn-shop-ch4s3-04-1920x1080-785ce1d90213.png";
break;
case 27:
bgs[0]["stage"] = "rufus";
break;
default:
if (s < 9 || s > 19) {
bgs[0]["stage"] = "defaultnotris";
bgs[0]["backgroundimage"] = "https://fortnite-public-service-prod11.ol.epicgames.com/images/lightlobbybg.png";
}
break;
}
// Build-specific overrides
if (b == 9.30f) {
cp["lobby"]["stage"] = "summer";
} else if (b == 11.10f) {
bgs[0]["stage"] = "fortnitemares";
bgs[1]["stage"] = "fortnitemares";
} else if (b == 11.31f || b == 11.40f) {
bgs[0]["stage"] = "winter19";
bgs[1]["stage"] = "winter19";
} else if (b == 19.01f) {
bgs[0]["stage"] = "winter2021";
bgs[0]["backgroundimage"] = "https://cdn2.unrealengine.com/t-bp19-lobby-xmas-2048x1024-f85d2684b4af.png";
cp["subgameinfo"]["battleroyale"]["image"] = "https://cdn2.unrealengine.com/19br-wf-subgame-select-512x1024-16d8bb0f218f.jpg";
cp["specialoffervideo"]["bSpecialOfferEnabled"] = "true";
} else if (b == 20.40f) {
bgs[0]["backgroundimage"] = "https://cdn2.unrealengine.com/t-bp20-40-armadillo-glowup-lobby-2048x2048-2048x2048-3b83b887cc7f.jpg";
} else if (b == 21.10f) {
bgs[0]["stage"] = "season2100";
} else if (b == 21.30f) {
bgs[0]["backgroundimage"] = "https://cdn2.unrealengine.com/nss-lobbybackground-2048x1024-f74a14565061.jpg";
bgs[0]["stage"] = "season2130";
} else if (b == 23.10f) {
bgs[0]["backgroundimage"] = "https://cdn2.unrealengine.com/t-bp23-winterfest-lobby-square-2048x2048-2048x2048-277a476e5ca6.png";
cp["specialoffervideo"]["bSpecialOfferEnabled"] = "true";
} else if (b == 25.11f) {
bgs[0]["backgroundimage"] = "https://cdn2.unrealengine.com/t-s25-14dos-lobby-4096x2048-2be24969eee3.jpg";
}
// Older builds use external image for news
if (b < 5.30f) {
const char* im0 = "https://fortnite-public-service-prod11.ol.epicgames.com/images/discord-s.png";
const char* im1 = "https://fortnite-public-service-prod11.ol.epicgames.com/images/embeddium-s.png";
cp["savetheworldnews"]["news"]["messages"][0]["image"] = im0;
cp["savetheworldnews"]["news"]["messages"][1]["image"] = im1;
cp["battleroyalenews"]["news"]["messages"][0]["image"] = im0;
cp["battleroyalenews"]["news"]["messages"][1]["image"] = im1;
}
} catch (...) {}
}
Json ContentService::Build(const VersionInfo& ver,
std::string_view acceptLanguage) {
Json cp = JsonUtils::ParseSafe(AssetManager::GetJSON("contentpages.json"));
if (!cp.is_object()) return Json::object();
// Resolve language
std::string lang = "en";
if (!acceptLanguage.empty()) {
std::string al(acceptLanguage);
if (al != "es-419" && al != "pt-BR") {
auto dash = al.find('-');
lang = dash != std::string::npos ? al.substr(0, dash) : al;
} else {
lang = al;
}
}
ChooseTranslations(cp, lang);
PatchBackgrounds(cp, ver);
return cp;
}
} // namespace Embeddium
+23
View File
@@ -0,0 +1,23 @@
#pragma once
#include "../Utilities/JsonUtils.h"
#include "VersionService.h"
namespace Embeddium {
// ─────────────────────────────────────────────────────────────────────────────
// ContentService
// Builds the /content/api/pages/fortnite-game response.
// Patches dynamic backgrounds, lobby stages, and translations based on
// the requesting game version.
// ─────────────────────────────────────────────────────────────────────────────
class ContentService {
public:
static Json Build(const VersionInfo& ver,
std::string_view acceptLanguage);
private:
static void ChooseTranslations(Json& obj, std::string_view lang);
static void PatchBackgrounds(Json& contentPages, const VersionInfo& ver);
};
} // namespace Embeddium
+91
View File
@@ -0,0 +1,91 @@
#include "FriendStore.h"
#include "../Assets/AssetManager.h"
#include <chrono>
#include <sstream>
#include <iomanip>
namespace Embeddium {
Json FriendStore::sFriendList_;
Json FriendStore::sFriendList2_;
static std::string NowIso() {
auto now = std::chrono::system_clock::now();
auto t = std::chrono::system_clock::to_time_t(now);
std::tm tm{};
#ifdef _WIN32
gmtime_s(&tm, &t);
#else
gmtime_r(&t, &tm);
#endif
std::ostringstream ss;
ss << std::put_time(&tm, "%Y-%m-%dT%H:%M:%S.000Z");
return ss.str();
}
void FriendStore::Init() {
sFriendList_ = JsonUtils::ParseSafe(AssetManager::GetJSON("friendslist.json"));
sFriendList2_ = JsonUtils::ParseSafe(AssetManager::GetJSON("friendslist2.json"));
if (!sFriendList_.is_array()) sFriendList_ = Json::array();
if (!sFriendList2_.is_object()) sFriendList2_ = Json::object();
if (!sFriendList2_.contains("friends"))
sFriendList2_["friends"] = Json::array();
}
bool FriendStore::HasFriend(std::string_view accountId) {
for (const auto& f : sFriendList_) {
if (f.value("accountId", "") == accountId) return true;
}
return false;
}
void FriendStore::AddFriendV1(std::string_view accountId) {
std::string now = NowIso();
sFriendList_.push_back({
{"accountId", std::string(accountId)},
{"status", "ACCEPTED"},
{"direction", "OUTBOUND"},
{"created", now},
{"favorite", false}
});
}
void FriendStore::AddFriendV2(std::string_view accountId) {
std::string now = NowIso();
sFriendList2_["friends"].push_back({
{"accountId", std::string(accountId)},
{"groups", Json::array()},
{"mutual", 0},
{"alias", ""},
{"note", ""},
{"favorite", false},
{"created", now}
});
}
Json FriendStore::GetFriendList(std::string_view accountId) {
if (!HasFriend(accountId)) {
AddFriendV1(accountId);
AddFriendV2(accountId);
}
return sFriendList_;
}
Json FriendStore::GetFriendSummary(std::string_view accountId) {
// Check presence in v2 list
bool found = false;
for (const auto& f : sFriendList2_["friends"]) {
if (f.value("accountId", "") == accountId) { found = true; break; }
}
if (!found) {
AddFriendV2(accountId);
if (!HasFriend(accountId)) AddFriendV1(accountId);
}
return sFriendList2_;
}
} // namespace Embeddium
+43
View File
@@ -0,0 +1,43 @@
#pragma once
#include "../Utilities/JsonUtils.h"
#include <string>
#include <string_view>
#include <vector>
namespace Embeddium {
// ─────────────────────────────────────────────────────────────────────────────
// FriendStore
// In-memory friend list populated from embedded friendslist.json and
// friendslist2.json assets at startup.
//
// Thread safety: this is a single-player server — only one game session
// connects at a time, so all HTTP requests are effectively serialized.
// All access goes through the io_context and is strand-serialized per
// session; since there is only one active session, no data races occur.
// ─────────────────────────────────────────────────────────────────────────────
class FriendStore {
public:
// Load from embedded assets "friendslist.json" and "friendslist2.json".
static void Init();
// Returns a copy of friendslist (array of friend objects).
// If the given accountId is not present, auto-adds it as ACCEPTED.
static Json GetFriendList(std::string_view accountId);
// Returns a copy of friendslist2 (object with "friends" array + metadata).
// If the given accountId is not present, auto-adds it.
static Json GetFriendSummary(std::string_view accountId);
// Whether the account exists in the v1 friend list
static bool HasFriend(std::string_view accountId);
private:
static void AddFriendV1(std::string_view accountId);
static void AddFriendV2(std::string_view accountId);
static Json sFriendList_; // array
static Json sFriendList2_; // object {friends:[...]}
};
} // namespace Embeddium
+166
View File
@@ -0,0 +1,166 @@
#include "ItemShopService.h"
#include "../Assets/AssetManager.h"
namespace Embeddium {
Json ItemShopService::sCatalog_;
void ItemShopService::Init() {
sCatalog_ = JsonUtils::ParseSafe(AssetManager::GetJSON("catalog.json"));
Json config = JsonUtils::ParseSafe(AssetManager::GetJSON("catalog_config.json"));
if (!sCatalog_.is_object() || !config.is_object()) return;
auto makeCatalogEntry = []() -> Json {
return {
{"devName", ""},
{"offerId", ""},
{"fulfillmentIds", Json::array()},
{"dailyLimit", -1},
{"weeklyLimit", -1},
{"monthlyLimit", -1},
{"categories", Json::array()},
{"prices", Json::array({
{
{"currencyType", "MtxCurrency"},
{"currencySubType", ""},
{"regularPrice", 0},
{"finalPrice", 0},
{"saleExpiration", "9999-12-02T01:12:00Z"},
{"basePrice", 0}
}
})},
{"meta", {
{"SectionId", "Featured"},
{"LayoutId", "Embeddium.99"},
{"TileSize", "Small"},
{"AnalyticOfferGroupId", "Embeddium/Attitude8"},
{"FirstSeen", "2/2/2020"},
{"inDate", "2018-04-30T00:00:00.000Z"},
{"outDate", "9999-12-31T23:59:59.999Z"},
{"color1", "#50C878"},
{"color2", "#1B5E20"},
{"textBackgroundColor", "#0D3D0D"}
}},
{"matchFilter", ""},
{"filterWeight", 0},
{"appStoreId", Json::array()},
{"requirements", Json::array()},
{"offerType", "StaticPrice"},
{"giftInfo", {
{"bIsEnabled", false},
{"forcedGiftBoxTemplateId", ""},
{"purchaseRequirements", Json::array()},
{"giftRecordIds", Json::array()}
}},
{"refundable", true},
{"metaInfo", Json::array({
{{"key","SectionId"},{"value","Featured"}},
{{"key","LayoutId"},{"value","Embeddium.99"}},
{{"key","TileSize"},{"value","Small"}},
{{"key","AnalyticOfferGroupId"},{"value","Embeddium/Attitude8"}},
{{"key","FirstSeen"},{"value","2/2/2020"}},
{{"key","inDate"},{"value","2018-04-30T00:00:00.000Z"}},
{{"key","outDate"},{"value","9999-12-31T23:59:59.999Z"}},
{{"key","color1"},{"value","#50C878"}},
{{"key","color2"},{"value","#1B5E20"}},
{{"key","textBackgroundColor"},{"value","#0D3D0D"}}
})},
{"displayAssetPath", ""},
{"itemGrants", Json::array()},
{"sortPriority", 0},
{"catalogGroupPriority", 0}
};
};
// Find storefront indices once
int dailyIdx = -1;
int featuredIdx = -1;
if (sCatalog_.contains("storefronts") && sCatalog_["storefronts"].is_array()) {
for (int i = 0; i < (int)sCatalog_["storefronts"].size(); ++i) {
const auto& sf = sCatalog_["storefronts"][i];
std::string name = sf.value("name", "");
if (name == "BRDailyStorefront") dailyIdx = i;
if (name == "BRWeeklyStorefront") featuredIdx = i;
}
}
try {
for (auto& [key, val] : config.items()) {
if (!val.is_object()) continue;
auto itemGrants = val.value("itemGrants", Json::array());
if (!itemGrants.is_array() || itemGrants.empty()) continue;
bool isDaily = key.size() >= 5 &&
(key[0]=='d'||key[0]=='D') &&
(key.substr(0,5) == "daily" || key.substr(0,5) == "Daily");
bool isFeatured = key.size() >= 8 &&
(key[0]=='f'||key[0]=='F') &&
(key.substr(0,8) == "featured" || key.substr(0,8) == "Featured");
int targetIdx = isDaily ? dailyIdx : (isFeatured ? featuredIdx : -1);
if (targetIdx < 0) continue;
Json entry = makeCatalogEntry();
entry["requirements"] = Json::array();
entry["itemGrants"] = Json::array();
for (const auto& grant : itemGrants) {
if (!grant.is_string()) continue;
std::string tpl = grant.get<std::string>();
if (tpl.empty()) continue;
if (entry["devName"].get<std::string>().empty()) {
entry["devName"] = tpl;
entry["offerId"] = tpl;
}
entry["requirements"].push_back({
{"requirementType", "DenyOnItemOwnership"},
{"requiredId", tpl},
{"minQuantity", 1}
});
entry["itemGrants"].push_back({
{"templateId", tpl},
{"quantity", 1}
});
entry["meta"]["templateId"] = tpl;
entry["metaInfo"].push_back({{"key","templateId"},{"value",tpl}});
}
int price = val.value("price", 0);
entry["prices"][0]["basePrice"] = price;
entry["prices"][0]["regularPrice"] = price;
entry["prices"][0]["finalPrice"] = price;
if (isFeatured) {
entry["meta"]["TileSize"] = "Normal";
entry["metaInfo"][2]["value"] = "Normal";
} else {
entry["sortPriority"] = -1;
}
// Optional display assets
std::string dap = val.value("displayAssetPath", "");
if (!dap.empty()) {
entry["meta"]["displayAssetPath"] = dap;
entry["metaInfo"].push_back({{"key","displayAssetPath"},{"value",dap}});
}
std::string ndap = val.value("NewDisplayAssetPath", "");
if (!ndap.empty()) {
entry["meta"]["NewDisplayAssetPath"] = ndap;
entry["metaInfo"].push_back({{"key","NewDisplayAssetPath"},{"value",ndap}});
}
if (!entry["itemGrants"].empty()) {
sCatalog_["storefronts"][targetIdx]["catalogEntries"].push_back(entry);
}
}
} catch (...) {}
}
const Json& ItemShopService::GetCatalog() {
return sCatalog_;
}
} // namespace Embeddium
+23
View File
@@ -0,0 +1,23 @@
#pragma once
#include "../Utilities/JsonUtils.h"
namespace Embeddium {
// ─────────────────────────────────────────────────────────────────────────────
// ItemShopService
// Builds the full storefront catalog response by merging the embedded
// catalog.json template with catalog_config.json daily/featured entries.
// ─────────────────────────────────────────────────────────────────────────────
class ItemShopService {
public:
// Build catalog once at startup; result is immutable after that.
static void Init();
// Returns the pre-built catalog JSON
static const Json& GetCatalog();
private:
static Json sCatalog_;
};
} // namespace Embeddium
+106
View File
@@ -0,0 +1,106 @@
#include "../Network/WebSocketServer.h"
#include "../Core/Config.h"
#include "../Core/Log.h"
#include "../Utilities/JsonUtils.h"
#include "../Utilities/UuidGenerator.h"
#include <memory>
#include <string>
#include <functional>
#include <thread>
#include <chrono>
// Drives the Fortnite matchmaking state machine over WebSocket.
// States sent to client: Waiting -> Queued -> SessionAssignment
// After SessionAssignment the game connects to gameServerIp:gameServerPort.
namespace Embeddium::MatchmakerServer {
namespace {
struct MmState {
WebSocketSession::Ptr ws;
std::string gameIp;
int gamePort;
explicit MmState(WebSocketSession::Ptr s) : ws(std::move(s)) {
Config cfg = ConfigLoader::LoadEmbedded();
gameIp = cfg.gameServerIp;
gamePort = cfg.gameServerPort;
Log::Debug("Matchmaker: session #" + std::to_string(ws->Id()) + " connected");
}
void Send(const Json& j) { ws->SendText(j.dump()); }
void OnMessage(std::shared_ptr<MmState> self, const std::string& raw) {
Json msg = JsonUtils::ParseSafe(raw);
if (!msg.is_object()) return;
std::string name = msg.value("name", "");
if (name != "StatusUpdate") return;
auto& payload = msg["payload"];
if (!payload.is_object()) return;
std::string state = payload.value("state", "");
if (state != "Initialize") return;
Log::Debug("Matchmaker: Initialize -> Waiting");
Send({{"name","StatusUpdate"},{"payload",{
{"state","Waiting"},
{"totalPlayers",1},
{"connectedPlayers",1}
}}});
// Advance through states on a detached thread
std::weak_ptr<MmState> weak(self);
std::thread([weak]() {
std::this_thread::sleep_for(std::chrono::milliseconds(500));
auto s = weak.lock();
if (!s) return;
std::string ticketId = UuidGenerator::MakeIDNoDashes();
std::string matchId = UuidGenerator::MakeIDNoDashes();
Log::Debug("Matchmaker: Waiting -> Queued");
s->Send({{"name","StatusUpdate"},{"payload",{
{"state","Queued"},
{"ticketId",ticketId},
{"estimatedWaitSec",1},
{"status",Json::object()}
}}});
std::this_thread::sleep_for(std::chrono::milliseconds(500));
s = weak.lock();
if (!s) return;
Log::Debug("Matchmaker: Queued -> SessionAssignment (server: "
+ s->gameIp + ":" + std::to_string(s->gamePort) + ")");
s->Send({{"name","StatusUpdate"},{"payload",{
{"state","SessionAssignment"},
{"matchId",matchId},
{"sessionId",matchId}
}}});
// Let the client read the assignment before we close
std::this_thread::sleep_for(std::chrono::milliseconds(250));
s = weak.lock();
if (s) s->ws->Close();
}).detach();
}
};
}
void OnSession(WebSocketSession::Ptr session) {
auto state = std::make_shared<MmState>(session);
session->SetMessageHandler([state](std::string_view msg) mutable {
state->OnMessage(state, std::string(msg));
});
session->SetCloseHandler([state]() { (void)state; });
}
} // namespace Embeddium::MatchmakerServer
+24
View File
@@ -0,0 +1,24 @@
#include "PrivacyStore.h"
#include "../Assets/AssetManager.h"
namespace Embeddium {
Json PrivacyStore::sPrivacy_;
void PrivacyStore::Init() {
sPrivacy_ = JsonUtils::ParseSafe(AssetManager::GetJSON("privacy.json"));
}
Json PrivacyStore::Get(std::string_view accountId) {
Json copy = sPrivacy_;
copy["accountId"] = std::string(accountId);
return copy;
}
Json PrivacyStore::SetOptOut(std::string_view accountId, bool optOut) {
sPrivacy_["accountId"] = std::string(accountId);
sPrivacy_["optOutOfPublicLeaderboards"] = optOut;
return sPrivacy_;
}
} // namespace Embeddium
+27
View File
@@ -0,0 +1,27 @@
#pragma once
#include "../Utilities/JsonUtils.h"
#include <string_view>
namespace Embeddium {
// ─────────────────────────────────────────────────────────────────────────────
// PrivacyStore
// Holds the privacy settings object (privacy.json).
// accountId is patched in per-request; optOutOfPublicLeaderboards can be
// updated via the POST endpoint.
// ─────────────────────────────────────────────────────────────────────────────
class PrivacyStore {
public:
static void Init();
// Returns the privacy object with accountId injected
static Json Get(std::string_view accountId);
// Updates optOutOfPublicLeaderboards and returns the new object
static Json SetOptOut(std::string_view accountId, bool optOut);
private:
static Json sPrivacy_;
};
} // namespace Embeddium
+127
View File
@@ -0,0 +1,127 @@
#include "ProfileStore.h"
#include "../Assets/AssetManager.h"
#include "../Utilities/JsonUtils.h"
#include <chrono>
#include <sstream>
#include <iomanip>
namespace Embeddium {
std::unordered_map<std::string, Json> ProfileStore::sProfiles_;
static std::string NowIso() {
auto now = std::chrono::system_clock::now();
auto t = std::chrono::system_clock::to_time_t(now);
std::tm tm{};
#ifdef _WIN32
gmtime_s(&tm, &t);
#else
gmtime_r(&t, &tm);
#endif
std::ostringstream ss;
ss << std::put_time(&tm, "%Y-%m-%dT%H:%M:%S.000Z");
return ss.str();
}
void ProfileStore::LoadProfile(std::string_view profileId) {
std::string assetKey = "profiles/" + std::string(profileId) + ".json";
auto text = AssetManager::GetJSON(assetKey);
Json j = JsonUtils::ParseSafe(text);
if (!j.is_object()) j = Json::object();
// Ensure mandatory fields exist
if (!j.contains("rvn")) j["rvn"] = 0;
if (!j.contains("commandRevision")) j["commandRevision"] = 0;
if (!j.contains("items")) j["items"] = Json::object();
if (!j.contains("stats")) j["stats"] = Json::object();
if (!j["stats"].contains("attributes"))
j["stats"]["attributes"] = Json::object();
sProfiles_[std::string(profileId)] = std::move(j);
}
void ProfileStore::ApplyAthenaSeason(Json& athena, int season, float build) {
// Mirror mcp.js middleware: patch athena stats with season-specific data
auto seasonDataText = AssetManager::GetJSON("Athena/SeasonData.json");
Json seasonData = JsonUtils::ParseSafe(seasonDataText);
athena["stats"]["attributes"]["season_num"] = season;
std::string key = "Season" + std::to_string(season);
if (seasonData.contains(key)) {
const auto& sd = seasonData[key];
athena["stats"]["attributes"]["book_purchased"] =
JsonUtils::GetBool(sd, "battlePassPurchased", false);
athena["stats"]["attributes"]["book_level"] =
JsonUtils::GetInt(sd, "battlePassTier", 1);
athena["stats"]["attributes"]["season_match_boost"] =
JsonUtils::GetInt(sd, "battlePassXPBoost", 0);
athena["stats"]["attributes"]["season_friend_match_boost"] =
JsonUtils::GetInt(sd, "battlePassXPFriendBoost", 0);
}
(void)build; // reserved for future build-specific patches
}
void ProfileStore::Init(int season, float build) {
for (const auto* name : kProfileNames) {
LoadProfile(name);
}
// Apply season-specific stat defaults to athena profile
auto it = sProfiles_.find("athena");
if (it != sProfiles_.end()) {
ApplyAthenaSeason(it->second, season, build);
}
}
Json* ProfileStore::GetProfile(std::string_view profileId) {
auto it = sProfiles_.find(std::string(profileId));
return it != sProfiles_.end() ? &it->second : nullptr;
}
int ProfileStore::BumpRevision(std::string_view profileId) {
Json* p = GetProfile(profileId);
if (!p) return -1;
int newRvn = (*p)["rvn"].get<int>() + 1;
(*p)["rvn"] = newRvn;
(*p)["commandRevision"] = (*p)["commandRevision"].get<int>() + 1;
return newRvn;
}
Json ProfileStore::BuildResponse(std::string_view profileId,
int queryRevision,
Json applyChanges) {
Json* p = GetProfile(profileId);
if (!p) {
return {{"error", "Profile not found"}};
}
int currentRvn = (*p)["rvn"].get<int>();
int commandRev = (*p)["commandRevision"].get<int>();
int baseRevision = currentRvn; // before any bump that just happened
// If client is out of sync, send a full profile update
if (queryRevision != -1 && queryRevision != currentRvn - 1
&& !applyChanges.empty()) {
applyChanges = Json::array();
applyChanges.push_back({
{"changeType", "fullProfileUpdate"},
{"profile", *p}
});
}
return {
{"profileRevision", currentRvn},
{"profileId", std::string(profileId)},
{"profileChangesBaseRevision", baseRevision},
{"profileChanges", applyChanges},
{"profileCommandRevision", commandRev},
{"serverTime", NowIso()},
{"responseVersion", 1}
};
}
} // namespace Embeddium
+60
View File
@@ -0,0 +1,60 @@
#pragma once
#include "../Utilities/JsonUtils.h"
#include <string>
#include <string_view>
#include <unordered_map>
#include <optional>
#include <functional>
namespace Embeddium {
// ─────────────────────────────────────────────────────────────────────────────
// ProfileStore
// Manages all 12 MCP profiles in memory. Loaded from embedded assets at
// startup; mutated by MCP operations.
//
// All profile access is synchronous — safe for single-session DLL use.
//
// Revision model:
// profile.rvn — incremented on each mutation
// profile.commandRevision — incremented alongside rvn
//
// The MCP response pattern:
// If queryRevision == profileRevision:
// return specific ApplyProfileChanges entries
// Else:
// return [{changeType:"fullProfileUpdate", profile: <profile>}]
// ─────────────────────────────────────────────────────────────────────────────
class ProfileStore {
public:
static void Init(int season, float build);
// Get a reference to a profile by name (e.g. "athena", "common_core")
// Returns nullptr if not found
static Json* GetProfile(std::string_view profileId);
// Increment rvn + commandRevision on the named profile
// Returns the new revision, or -1 if not found
static int BumpRevision(std::string_view profileId);
// Build an MCP response envelope
// If queryRevision != profile.rvn, returns fullProfileUpdate
static Json BuildResponse(std::string_view profileId,
int queryRevision,
Json applyChanges);
// Known profile names
static constexpr const char* kProfileNames[] = {
"athena", "common_core", "common_public", "campaign",
"profile0", "collection_book_people0", "collection_book_schematics0",
"collections", "creative", "metadata", "outpost0", "theater0"
};
private:
static void LoadProfile(std::string_view profileId);
static void ApplyAthenaSeason(Json& athena, int season, float build);
static std::unordered_map<std::string, Json> sProfiles_;
};
} // namespace Embeddium
+97
View File
@@ -0,0 +1,97 @@
#include "TheaterService.h"
#include "../Assets/AssetManager.h"
#include "../Utilities/StringUtils.h"
#include <chrono>
#include <sstream>
#include <iomanip>
namespace Embeddium {
Json TheaterService::Build(const VersionInfo& ver) {
auto text = AssetManager::GetText("Campaign/worldstw.json");
if (text.empty()) return Json::object();
// Apply string-level patches before parsing (mirrors JS string.replace())
std::string theater(text);
float b = ver.build;
// 30.20+ path migration
if (b >= 30.20f) {
theater = StringUtils::ReplaceAllI(theater,
"/Game/World/ZoneThemes",
"/STW_Zones/World/ZoneThemes");
theater = StringUtils::ReplaceAllI(theater,
"\"DataTable'/Game/",
"\"/Script/Engine.DataTable'/Game/");
}
// 15.30+: /Game/ → /SaveTheWorld/ (but keep DataTables on /Game/)
if (b >= 15.30f) {
theater = StringUtils::ReplaceAllI(theater,
"/Game/",
"/SaveTheWorld/");
theater = StringUtils::ReplaceAllI(theater,
"\"DataTable'/SaveTheWorld/",
"\"DataTable'/Game/");
}
// Patch mission refresh date
{
auto now = std::chrono::system_clock::now();
auto time = std::chrono::system_clock::to_time_t(now);
std::tm tm{};
gmtime_s(&tm, &time);
int hour = tm.tm_hour;
if (ver.season >= 9) {
tm.tm_hour = 23; tm.tm_min = 59; tm.tm_sec = 59;
} else {
if (hour < 6) { tm.tm_hour = 5; tm.tm_min = 59; tm.tm_sec = 59; }
else if (hour < 12) { tm.tm_hour = 11; tm.tm_min = 59; tm.tm_sec = 59; }
else if (hour < 18) { tm.tm_hour = 17; tm.tm_min = 59; tm.tm_sec = 59; }
else { tm.tm_hour = 23; tm.tm_min = 59; tm.tm_sec = 59; }
}
// milliseconds
std::ostringstream ss;
ss << std::put_time(&tm, "%Y-%m-%dT%H:%M:%S.999Z");
theater = StringUtils::ReplaceAllI(theater,
"2017-07-25T23:59:59.999Z", ss.str());
}
Json j = JsonUtils::ParseSafe(theater);
if (!j.is_object()) return Json::object();
// Merge seasonal sub-data into main arrays
if (j.contains("Seasonal") && j["Seasonal"].is_object()) {
std::string seasonKey = "Season" + std::to_string(ver.season);
if (j["Seasonal"].contains(seasonKey)) {
const auto& sd = j["Seasonal"][seasonKey];
if (sd.contains("theaters") && sd["theaters"].is_array()) {
if (!j.contains("theaters") || !j["theaters"].is_array())
j["theaters"] = Json::array();
for (const auto& t : sd["theaters"])
j["theaters"].push_back(t);
}
if (sd.contains("missions") && sd["missions"].is_array()) {
if (!j.contains("missions") || !j["missions"].is_array())
j["missions"] = Json::array();
for (const auto& m : sd["missions"])
j["missions"].push_back(m);
}
if (sd.contains("missionAlerts") && sd["missionAlerts"].is_array()) {
if (!j.contains("missionAlerts") || !j["missionAlerts"].is_array())
j["missionAlerts"] = Json::array();
for (const auto& ma : sd["missionAlerts"])
j["missionAlerts"].push_back(ma);
}
}
j.erase("Seasonal");
}
return j;
}
} // namespace Embeddium
+17
View File
@@ -0,0 +1,17 @@
#pragma once
#include "../Utilities/JsonUtils.h"
#include "VersionService.h"
namespace Embeddium {
// ─────────────────────────────────────────────────────────────────────────────
// TheaterService
// Builds the StW theater response from the embedded worldstw.json asset.
// Applies per-version path patches and merges seasonal theater/mission data.
// ─────────────────────────────────────────────────────────────────────────────
class TheaterService {
public:
static Json Build(const VersionInfo& ver);
};
} // namespace Embeddium
+322
View File
@@ -0,0 +1,322 @@
#include "TimelineService.h"
#include <chrono>
#include <sstream>
#include <iomanip>
namespace Embeddium {
static std::string NowIso() {
auto now = std::chrono::system_clock::now();
auto t = std::chrono::system_clock::to_time_t(now);
std::tm tm{};
#ifdef _WIN32
gmtime_s(&tm, &t);
#else
gmtime_r(&t, &tm);
#endif
std::ostringstream ss;
ss << std::put_time(&tm, "%Y-%m-%dT%H:%M:%S.000Z");
return ss.str();
}
Json TimelineService::MakeEvent(const char* eventType) {
return {
{"eventType", eventType},
{"activeUntil", "9999-01-01T00:00:00.000Z"},
{"activeSince", "2020-01-01T00:00:00.000Z"}
};
}
void TimelineService::AddSeasonEvents(Json& events, const VersionInfo& ver,
const Config& cfg) {
float b = ver.build;
int s = ver.season;
switch (s) {
case 3:
events.push_back(MakeEvent("EventFlag.Spring2018Phase1"));
if (b >= 3.1f) events.push_back(MakeEvent("EventFlag.Spring2018Phase2"));
if (b >= 3.3f) events.push_back(MakeEvent("EventFlag.Spring2018Phase3"));
if (b >= 3.4f) events.push_back(MakeEvent("EventFlag.Spring2018Phase4"));
break;
case 4:
events.push_back(MakeEvent("EventFlag.Blockbuster2018"));
events.push_back(MakeEvent("EventFlag.Blockbuster2018Phase1"));
if (b >= 4.3f) events.push_back(MakeEvent("EventFlag.Blockbuster2018Phase2"));
if (b >= 4.4f) events.push_back(MakeEvent("EventFlag.Blockbuster2018Phase3"));
if (b >= 4.5f) events.push_back(MakeEvent("EventFlag.Blockbuster2018Phase4"));
if (cfg.bEnableBlockbusterRiskyEvent)
events.push_back(MakeEvent("EventFlag.Blockbuster2018Phase5"));
break;
case 5:
events.push_back(MakeEvent("EventFlag.RoadTrip2018"));
events.push_back(MakeEvent("EventFlag.Horde"));
events.push_back(MakeEvent("EventFlag.Anniversary2018_BR"));
events.push_back(MakeEvent("EventFlag.LTM_Heist"));
if (b == 5.10f) events.push_back(MakeEvent("EventFlag.BirthdayBattleBus"));
break;
case 6:
events.push_back(MakeEvent("EventFlag.LTM_Fortnitemares"));
events.push_back(MakeEvent("EventFlag.LTM_LilKevin"));
if (b >= 6.20f) {
events.push_back(MakeEvent("EventFlag.Fortnitemares"));
events.push_back(MakeEvent("EventFlag.FortnitemaresPhase1"));
events.push_back(MakeEvent("POI0"));
}
if (b >= 6.22f) events.push_back(MakeEvent("EventFlag.FortnitemaresPhase2"));
if (b == 6.20f || b == 6.21f) {
events.push_back(MakeEvent("EventFlag.LobbySeason6Halloween"));
events.push_back(MakeEvent("EventFlag.HalloweenBattleBus"));
}
break;
case 7:
events.push_back(MakeEvent("EventFlag.Frostnite"));
events.push_back(MakeEvent("EventFlag.LTM_14DaysOfFortnite"));
events.push_back(MakeEvent("EventFlag.LTE_Festivus"));
events.push_back(MakeEvent("EventFlag.LTM_WinterDeimos"));
events.push_back(MakeEvent("EventFlag.LTE_S7_OverTime"));
break;
case 8:
events.push_back(MakeEvent("EventFlag.Spring2019"));
events.push_back(MakeEvent("EventFlag.Spring2019.Phase1"));
events.push_back(MakeEvent("EventFlag.LTM_Ashton"));
events.push_back(MakeEvent("EventFlag.LTM_Goose"));
events.push_back(MakeEvent("EventFlag.LTM_HighStakes"));
events.push_back(MakeEvent("EventFlag.LTE_BootyBay"));
if (b >= 8.2f) events.push_back(MakeEvent("EventFlag.Spring2019.Phase2"));
break;
case 9:
events.push_back(MakeEvent("EventFlag.Season9.Phase1"));
events.push_back(MakeEvent("EventFlag.Anniversary2019_BR"));
events.push_back(MakeEvent("EventFlag.LTM_14DaysOfSummer"));
events.push_back(MakeEvent("EventFlag.LTM_Mash"));
events.push_back(MakeEvent("EventFlag.LTM_Wax"));
if (b >= 9.2f) events.push_back(MakeEvent("EventFlag.Season9.Phase2"));
break;
case 10:
events.push_back(MakeEvent("EventFlag.Mayday"));
events.push_back(MakeEvent("EventFlag.Season10.Phase2"));
events.push_back(MakeEvent("EventFlag.Season10.Phase3"));
events.push_back(MakeEvent("EventFlag.LTE_BlackMonday"));
events.push_back(MakeEvent("EventFlag.S10_Oak"));
events.push_back(MakeEvent("EventFlag.S10_Mystery"));
for (int i = 1; i <= 10; ++i) {
std::string ef = "EventFlag.Season10_UrgentMission_" + std::to_string(i);
events.push_back(MakeEvent(ef.c_str()));
}
break;
case 11:
events.push_back(MakeEvent("EventFlag.LTE_CoinCollectXP"));
events.push_back(MakeEvent("EventFlag.LTE_Fortnitemares2019"));
events.push_back(MakeEvent("EventFlag.LTE_Galileo_Feats"));
events.push_back(MakeEvent("EventFlag.LTE_Galileo"));
events.push_back(MakeEvent("EventFlag.LTE_WinterFest2019"));
if (b >= 11.2f) events.push_back(MakeEvent("EventFlag.Starlight"));
if (b < 11.3f) {
if (b >= 11.01f) events.push_back(MakeEvent("EventFlag.Season11.Fortnitemares.Quests.Phase1"));
if (b >= 11.10f) {
events.push_back(MakeEvent("EventFlag.Season11.Fortnitemares.Quests.Phase2"));
events.push_back(MakeEvent("EventFlag.Season11.Fortnitemares.Quests.Phase3"));
events.push_back(MakeEvent("EventFlag.Season11.Fortnitemares.Quests.Phase4"));
events.push_back(MakeEvent("EventFlag.StormKing.Landmark"));
}
} else {
events.push_back(MakeEvent("EventFlag.HolidayDeco"));
events.push_back(MakeEvent("EventFlag.Season11.WinterFest.Quests.Phase1"));
events.push_back(MakeEvent("EventFlag.Season11.WinterFest.Quests.Phase2"));
events.push_back(MakeEvent("EventFlag.Season11.WinterFest.Quests.Phase3"));
events.push_back(MakeEvent("EventFlag.Season11.Frostnite"));
}
if (b == 11.31f || b == 11.40f) {
events.push_back(MakeEvent("EventFlag.Winterfest.Tree"));
events.push_back(MakeEvent("EventFlag.LTE_WinterFest"));
events.push_back(MakeEvent("EventFlag.LTE_WinterFest2019"));
}
break;
case 12:
events.push_back(MakeEvent("EventFlag.LTE_SpyGames"));
events.push_back(MakeEvent("EventFlag.LTE_JerkyChallenges"));
events.push_back(MakeEvent("EventFlag.LTE_Oro"));
events.push_back(MakeEvent("EventFlag.LTE_StormTheAgency"));
break;
case 14:
events.push_back(MakeEvent("EventFlag.LTE_Fortnitemares_2020"));
break;
case 15:
for (int i = 1; i <= 15; ++i) {
char buf[64];
snprintf(buf, sizeof(buf), "EventFlag.LTQ_S15_Legendary_Week_%02d", i);
events.push_back(MakeEvent(buf));
}
events.push_back(MakeEvent("EventFlag.Event_HiddenRole"));
events.push_back(MakeEvent("EventFlag.Event_OperationSnowdown"));
events.push_back(MakeEvent("EventFlag.Event_PlumRetro"));
break;
case 16:
for (int i = 1; i <= 12; ++i) {
char buf[64];
snprintf(buf, sizeof(buf), "EventFlag.LTQ_S16_Legendary_Week_%02d", i);
events.push_back(MakeEvent(buf));
}
events.push_back(MakeEvent("EventFlag.Event_NBA_Challenges"));
events.push_back(MakeEvent("EventFlag.Event_Spire_Challenges"));
break;
case 17:
events.push_back(MakeEvent("EventFlag.Event_TheMarch"));
events.push_back(MakeEvent("EventFlag.Event_FnCS_S17"));
for (int i = 1; i <= 16; ++i) {
char buf[64];
snprintf(buf, sizeof(buf), "EventFlag.LTQ_S17_Legendary_Week_%02d", i);
events.push_back(MakeEvent(buf));
}
break;
case 18:
events.push_back(MakeEvent("EventFlag.Event_Fortnitemares2021"));
events.push_back(MakeEvent("EventFlag.Event_Fortnitemares2021_Quests"));
events.push_back(MakeEvent("EventFlag.Event_Winterfest2021"));
events.push_back(MakeEvent("EventFlag.Event_Winterfest2021_Quests"));
events.push_back(MakeEvent("EventFlag.Event_Winterfest2021_FnCS"));
break;
case 19:
events.push_back(MakeEvent("EventFlag.Event_Winterfest2021"));
events.push_back(MakeEvent("EventFlag.Event_Winterfest2021_Quests"));
break;
default:
break;
}
// ── Config-driven optional events ────────────────────────────────────────
if (cfg.bEnableGeodeEvent) {
events.push_back({
{"eventType", "EventFlag.LTE_Geode"},
{"activeUntil", "9999-01-01T00:00:00.000Z"},
{"activeSince", cfg.geodeEventStartDate}
});
}
if (cfg.bEnableCrackInTheSky) {
events.push_back(MakeEvent("EventFlag.LTE_WatchTheSky"));
}
// Season 4 Oddity
if (cfg.bEnableS4OddityPrecursor) {
events.push_back({
{"eventType", "EventFlag.S4OddityPrecursor"},
{"activeUntil", "9999-01-01T00:00:00.000Z"},
{"activeSince", cfg.s4OddityEventStartDate}
});
}
if (cfg.bEnableS4OddityExecution) {
events.push_back({
{"eventType", "EventFlag.S4OddityExecution"},
{"activeUntil", "9999-01-01T00:00:00.000Z"},
{"activeSince", cfg.s4OddityEventStartDate}
});
}
// Season 5 Oddity
if (cfg.bEnableS5OddityPrecursor) {
events.push_back({
{"eventType", "EventFlag.S5OddityPrecursor"},
{"activeUntil", "9999-01-01T00:00:00.000Z"},
{"activeSince", cfg.s5OddityPrecursorDate}
});
}
if (cfg.bEnableS5OddityExecution) {
events.push_back({
{"eventType", "EventFlag.S5OddityExecution"},
{"activeUntil", "9999-01-01T00:00:00.000Z"},
{"activeSince", cfg.s5OddityExecutionDate}
});
}
// Cube events
if (cfg.bEnableCubeLightning) {
events.push_back({
{"eventType", "EventFlag.CubeLightning"},
{"activeUntil", "9999-01-01T00:00:00.000Z"},
{"activeSince", cfg.cubeSpawnDate}
});
}
if (cfg.bEnableCubeLake) {
events.push_back({
{"eventType", "EventFlag.CubeLake"},
{"activeUntil", "9999-01-01T00:00:00.000Z"},
{"activeSince", cfg.cubeLakeDate}
});
}
}
Json TimelineService::BuildTimeline(const VersionInfo& ver, const Config& cfg) {
std::string now = NowIso();
Json activeEvents = Json::array();
activeEvents.push_back({
{"eventType", "EventFlag.Season" + std::to_string(ver.season)},
{"activeUntil", "9999-01-01T00:00:00.000Z"},
{"activeSince", "2020-01-01T00:00:00.000Z"}
});
activeEvents.push_back({
{"eventType", "EventFlag." + ver.lobby},
{"activeUntil", "9999-01-01T00:00:00.000Z"},
{"activeSince", "2020-01-01T00:00:00.000Z"}
});
AddSeasonEvents(activeEvents, ver, cfg);
return {
{"channels", {
{"client-matchmaking", {
{"states", Json::array()},
{"cacheExpire", "9999-01-01T00:00:00.000Z"}
}},
{"client-events", {
{"states", Json::array({
{
{"validFrom", "2020-01-01T00:00:00.000Z"},
{"activeEvents", activeEvents},
{"state", {
{"activeStorefronts", Json::array()},
{"eventNamedWeights", Json::object()},
{"activeEvents", activeEvents},
{"seasonNumber", ver.season},
{"seasonTemplateId", "AthenaSeason:athenaseason" + std::to_string(ver.season)},
{"matchXpBonusPoints", 0},
{"eventPunchCardTemplateId", ""},
{"seasonBegin", "2020-01-01T00:00:00.000Z"},
{"seasonEnd", "9999-01-01T00:00:00.000Z"},
{"seasonDisplayedEnd","9999-01-01T00:00:00.000Z"},
{"weeklyStoreEnd", "9999-01-01T00:00:00.000Z"},
{"stwEventStoreEnd", "9999-01-01T00:00:00.000Z"},
{"stwWeeklyStoreEnd", "9999-01-01T00:00:00.000Z"},
{"sectionStoreEnds", Json::object()},
{"dailyStoreEnd", "9999-01-01T00:00:00.000Z"}
}}
}
})},
{"cacheExpire", "9999-01-01T00:00:00.000Z"}
}}
}},
{"cacheIntervalMins", 30},
{"currentTime", now}
};
}
} // namespace Embeddium
+23
View File
@@ -0,0 +1,23 @@
#pragma once
#include "../Utilities/JsonUtils.h"
#include "VersionService.h"
#include "../Core/Config.h"
namespace Embeddium {
// ─────────────────────────────────────────────────────────────────────────────
// TimelineService
// Builds /fortnite/api/calendar/v1/timeline responses.
// Season-specific event flags, optional Geode/CrackInTheSky/Oddity/Cube events from config.
// ─────────────────────────────────────────────────────────────────────────────
class TimelineService {
public:
static Json BuildTimeline(const VersionInfo& ver, const Config& cfg);
private:
static void AddSeasonEvents(Json& events, const VersionInfo& ver,
const Config& cfg);
static Json MakeEvent(const char* eventType);
};
} // namespace Embeddium
+92
View File
@@ -0,0 +1,92 @@
#include "VersionService.h"
#include "../Utilities/StringUtils.h"
#include <cstdlib>
#include <cmath>
namespace Embeddium {
// ─────────────────────────────────────────────────────────────────────────────
// Parses Fortnite User-Agent strings into VersionInfo.
//
// User-Agent examples:
// Fortnite/++Fortnite+Release-5.41-CL-4166579-Windows
// Fortnite/++Fortnite+Release-12.41-CL-12905909,netCL-12905909
//
// Parsing rules (from functions.js):
// CL: split on '-', take [3], strip trailing comma-anything
// fallback: split on '-', take [1], strip +anything
// Build: strip up to "Release-", take [0] after another '-',
// if has 3 dot-parts: compress to A.BC
// season: Build.split('.')[0]
// lobby: "LobbySeason{season}", fallback "LobbyWinterDecor"
// ─────────────────────────────────────────────────────────────────────────────
VersionInfo VersionService::Parse(std::string_view ua) {
VersionInfo out;
if (ua.empty()) return out;
std::string s(ua);
// ── CL extraction ────────────────────────────────────────────────────────
{
auto parts = StringUtils::Split(s, '-');
if (parts.size() >= 4) {
// Primary: parts[3] before comma
std::string cl = parts[3];
auto comma = cl.find(',');
if (comma != std::string::npos) cl = cl.substr(0, comma);
// Check if numeric
bool isNum = !cl.empty();
for (char c : cl) if (c < '0' || c > '9') { isNum = false; break; }
if (isNum) out.CL = cl;
}
if (out.CL.empty() && parts.size() >= 2) {
// Fallback: parts[1] before '+'
std::string cl = parts[1];
auto plus = cl.find('+');
if (plus != std::string::npos) cl = cl.substr(0, plus);
bool isNum = !cl.empty();
for (char c : cl) if (c < '0' || c > '9') { isNum = false; break; }
if (isNum) out.CL = cl;
}
}
// ── Build extraction ─────────────────────────────────────────────────────
{
auto relPos = s.find("Release-");
if (relPos != std::string::npos) {
std::string_view after = std::string_view(s).substr(relPos + 8);
// Take everything up to the next '-'
auto dash = after.find('-');
std::string buildStr = std::string(
dash != std::string_view::npos ? after.substr(0, dash) : after);
// If 3 parts (e.g. "5.41.0"), compress to "5.410"
auto dotParts = StringUtils::Split(buildStr, '.');
if (dotParts.size() == 3) {
buildStr = dotParts[0] + "." + dotParts[1] + dotParts[2];
}
try {
float build = std::stof(buildStr);
if (!std::isnan(build) && !std::isinf(build)) {
out.build = build;
out.season = static_cast<int>(build);
}
} catch (...) {}
}
}
// ── Lobby ────────────────────────────────────────────────────────────────
if (out.season > 0) {
out.lobby = "LobbySeason" + std::to_string(out.season);
} else {
out.lobby = "LobbyWinterDecor";
out.season = 2;
out.build = 2.0f;
}
return out;
}
} // namespace Embeddium
+28
View File
@@ -0,0 +1,28 @@
#pragma once
#include <string>
#include <string_view>
namespace Embeddium {
// ─────────────────────────────────────────────────────────────────────────────
// VersionInfo
// Parsed from the game's User-Agent header.
// ─────────────────────────────────────────────────────────────────────────────
struct VersionInfo {
int season = 2;
float build = 2.0f;
std::string CL;
std::string lobby = "LobbyWinterDecor";
};
// ─────────────────────────────────────────────────────────────────────────────
// VersionService
// Parses User-Agent → VersionInfo.
// ─────────────────────────────────────────────────────────────────────────────
class VersionService {
public:
// Parse "Fortnite/++Fortnite+Release-5.41-CL-4166579 ..." user-agent
static VersionInfo Parse(std::string_view userAgent);
};
} // namespace Embeddium
+130
View File
@@ -0,0 +1,130 @@
#include <string>
#include "Config.h"
#include "../Assets/AssetManager.h"
#include "../Utilities/StringUtils.h"
#include <unordered_map>
namespace Embeddium::ConfigLoader {
namespace {
// Very simple INI parser: supports [Section] and key=value pairs.
// Comments with # or ; are stripped. Section and key names are case-insensitive.
struct IniData {
// section -> (key -> value)
std::unordered_map<std::string,
std::unordered_map<std::string, std::string>> sections;
std::string Get(std::string_view section, std::string_view key,
std::string_view def = "") const {
std::string s = StringUtils::ToLower(section);
std::string k = StringUtils::ToLower(key);
auto sit = sections.find(s);
if (sit == sections.end()) return std::string(def);
auto kit = sit->second.find(k);
if (kit == sit->second.end()) return std::string(def);
return kit->second;
}
bool GetBool(std::string_view section, std::string_view key, bool def = false) const {
std::string v = StringUtils::ToLower(Get(section, key));
if (v == "true" || v == "1" || v == "yes") return true;
if (v == "false" || v == "0" || v == "no") return false;
return def;
}
int GetInt(std::string_view section, std::string_view key, int def = 0) const {
std::string v = Get(section, key);
if (v.empty()) return def;
try { return std::stoi(v); } catch (...) { return def; }
}
};
IniData ParseIni(std::string_view text) {
IniData data;
std::string currentSection;
size_t pos = 0;
while (pos <= text.size()) {
size_t nl = text.find('\n', pos);
if (nl == std::string_view::npos) nl = text.size();
std::string line(text.substr(pos, nl - pos));
pos = nl + 1;
// Strip \r from Windows line endings
if (!line.empty() && line.back() == '\r') line.pop_back();
// Strip comments
auto commentPos = line.find('#');
if (commentPos != std::string::npos) line.resize(commentPos);
commentPos = line.find(';');
if (commentPos != std::string::npos) line.resize(commentPos);
line = StringUtils::Trim(line);
if (line.empty()) continue;
if (line.front() == '[' && line.back() == ']') {
currentSection = StringUtils::ToLower(line.substr(1, line.size() - 2));
continue;
}
auto eq = line.find('=');
if (eq != std::string::npos) {
std::string key = StringUtils::ToLower(StringUtils::Trim(line.substr(0, eq)));
std::string value = StringUtils::Trim(line.substr(eq + 1));
data.sections[currentSection][key] = value;
}
}
return data;
}
}
Config Parse(std::string_view iniText) {
IniData ini = ParseIni(iniText);
Config cfg;
// [Config]
cfg.bUseConfigDisplayName = ini.GetBool("config", "bUseConfigDisplayName", false);
cfg.displayName = ini.Get("config", "displayName", "Embeddium");
// [Profile]
cfg.bCompletedSeasonalQuests = ini.GetBool("profile", "bCompletedSeasonalQuests", false);
cfg.bGrantFoundersPacks = ini.GetBool("profile", "bGrantFoundersPacks", false);
cfg.bAllSTWEventsActivated = ini.GetBool("profile", "bAllSTWEventsActivated", false);
// [GameServer]
cfg.gameServerIp = ini.Get("gameserver", "ip", "127.0.0.1");
cfg.gameServerPort = ini.GetInt("gameserver", "port", 7777);
// [Events]
cfg.bEnableGeodeEvent = ini.GetBool("events", "bEnableGeodeEvent", false);
cfg.geodeEventStartDate = ini.Get("events", "geodeEventStartDate", "2020-01-01T00:00:00.000Z");
cfg.bEnableCrackInTheSky = ini.GetBool("events", "bEnableCrackInTheSky", false);
cfg.bEnableS4OddityPrecursor = ini.GetBool("events", "bEnableS4OddityPrecursor", false);
cfg.bEnableS4OddityExecution = ini.GetBool("events", "bEnableS4OddityExecution", false);
cfg.s4OddityEventStartDate = ini.Get("events", "S4OddityEventStartDate", "2020-01-01T00:00:00.000Z");
cfg.s4OddityEventsInterval = ini.GetInt("events", "S4OddityEventsInterval", 0);
cfg.bEnableS5OddityPrecursor = ini.GetBool("events", "bEnableS5OddityPrecursor", false);
cfg.s5OddityPrecursorDate = ini.Get("events", "S5OddityPrecursorDate", "2020-01-01T00:00:00.000Z");
cfg.bEnableS5OddityExecution = ini.GetBool("events", "bEnableS5OddityExecution", false);
cfg.s5OddityExecutionDate = ini.Get("events", "S5OddityExecutionDate", "2020-01-01T00:00:00.000Z");
cfg.bEnableCubeLightning = ini.GetBool("events", "bEnableCubeLightning", false);
cfg.cubeSpawnDate = ini.Get("events", "cubeSpawnDate", "2020-01-01T00:00:00.000Z");
cfg.bEnableBlockbusterRiskyEvent = ini.GetBool("events", "bEnableBlockbusterRiskyEvent", false);
cfg.bEnableCubeLake = ini.GetBool("events", "bEnableCubeLake", false);
cfg.cubeLakeDate = ini.Get("events", "cubeLakeDate", "2020-01-01T00:00:00.000Z");
return cfg;
}
Config LoadEmbedded() {
auto text = AssetManager::GetText("config.ini");
return Parse(text);
}
} // namespace Embeddium::ConfigLoader
+74
View File
@@ -0,0 +1,74 @@
#pragma once
#include <string>
#include <string_view>
#include <atomic>
namespace Embeddium {
// ─────────────────────────────────────────────────────────────────────────────
// Config
// Holds all configuration values parsed from the embedded config.ini.
// Parsed once at startup; read-only after that.
// The user can override the persisted display name at runtime via
// AccountState — Config only holds the static startup values.
// ─────────────────────────────────────────────────────────────────────────────
struct Config {
// [Config]
bool bUseConfigDisplayName = false;
std::string displayName = "Embeddium";
// [Profile]
bool bCompletedSeasonalQuests = false;
bool bGrantFoundersPacks = false;
bool bAllSTWEventsActivated = false;
// [GameServer]
std::string gameServerIp = "127.0.0.1";
int gameServerPort = 7777;
// [Events]
bool bEnableGeodeEvent = false;
std::string geodeEventStartDate = "2020-01-01T00:00:00.000Z";
bool bEnableCrackInTheSky = false;
bool bEnableS4OddityPrecursor = false;
bool bEnableS4OddityExecution = false;
std::string s4OddityEventStartDate = "2020-01-01T00:00:00.000Z";
int s4OddityEventsInterval = 0;
bool bEnableS5OddityPrecursor = false;
std::string s5OddityPrecursorDate = "2020-01-01T00:00:00.000Z";
bool bEnableS5OddityExecution = false;
std::string s5OddityExecutionDate = "2020-01-01T00:00:00.000Z";
bool bEnableCubeLightning = false;
std::string cubeSpawnDate = "2020-01-01T00:00:00.000Z";
bool bEnableBlockbusterRiskyEvent = false;
bool bEnableCubeLake = false;
std::string cubeLakeDate = "2020-01-01T00:00:00.000Z";
// HTTP server port (always 3551, not configurable but kept here for clarity)
static constexpr int kHttpPort = 3551;
// XMPP/Matchmaker WS port
static constexpr int kWsPort = 80;
// Redirect target for curl hook
static constexpr const char* kRedirectProtocol = "http";
static constexpr const char* kRedirectHost = "127.0.0.1";
static constexpr const char* kRedirectPort = "3551";
};
// ─────────────────────────────────────────────────────────────────────────────
// ConfigLoader
// Parses an INI-format string into a Config struct.
// ─────────────────────────────────────────────────────────────────────────────
namespace ConfigLoader {
// Parse the given INI text (e.g. from embedded asset) into a Config.
Config Parse(std::string_view iniText);
// Load config from embedded asset "config.ini"
Config LoadEmbedded();
} // namespace ConfigLoader
} // namespace Embeddium
+61
View File
@@ -0,0 +1,61 @@
#include "Initializer.h"
#include "Log.h"
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#include <Windows.h>
static void WriteMarkerFile(const char* filename, const char* content) {
char path[MAX_PATH]{};
GetModuleFileNameA(nullptr, path, MAX_PATH);
for (int i = lstrlenA(path) - 1; i >= 0; --i) {
if (path[i] == '\\' || path[i] == '/') { path[i + 1] = '\0'; break; }
}
lstrcatA(path, filename);
HANDLE h = CreateFileA(path, GENERIC_WRITE, 0, nullptr,
CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr);
if (h == INVALID_HANDLE_VALUE) return;
DWORD written;
WriteFile(h, content, lstrlenA(content), &written, nullptr);
CloseHandle(h);
}
static DWORD WINAPI EmbeddiumMain(LPVOID) {
// SEH catches access violations and other hardware exceptions that
// C++ try/catch cannot intercept.
__try {
Embeddium::Initializer::Run();
} __except (EXCEPTION_EXECUTE_HANDLER) {
DWORD code = GetExceptionCode();
// If Log itself crashed this call is a no-op (file is closed).
char msg[128];
wsprintfA(msg, "FATAL: unhandled exception in EmbeddiumMain — code 0x%08X", (unsigned)code);
OutputDebugStringA(msg);
char crashMsg[256];
wsprintfA(crashMsg, "%s\r\nCheck Embeddium.log for last logged step.\r\n", msg);
WriteMarkerFile("Embeddium_crash.txt", crashMsg);
}
return 0;
}
BOOL APIENTRY DllMain(HMODULE hModule, DWORD reason, LPVOID /*reserved*/) {
switch (reason) {
case DLL_PROCESS_ATTACH:
WriteMarkerFile("Embeddium_dllmain.txt", "DLL_PROCESS_ATTACH fired\r\n");
DisableThreadLibraryCalls(hModule);
CreateThread(nullptr, 0, EmbeddiumMain, nullptr, 0, nullptr);
break;
case DLL_PROCESS_DETACH:
Embeddium::Initializer::Shutdown();
break;
default:
break;
}
return TRUE;
}
+205
View File
@@ -0,0 +1,205 @@
#include "Initializer.h"
#include "Log.h"
#include "Config.h"
#include "../Assets/AssetManager.h"
#include "../Hooks/HookManager.h"
#include "../Hooks/CurlHook.h"
#include "../Hooks/ExitHook.h"
#include "../HTTP/Router.h"
#include "../Network/HttpServer.h"
#include "../Network/WebSocketServer.h"
#include "../Backend/AccountState.h"
#include "../Backend/FriendStore.h"
#include "../Backend/PrivacyStore.h"
#include "../Backend/ProfileStore.h"
#include "../Backend/ItemShopService.h"
namespace Embeddium::Routes {
void RegisterAll(Router& router);
}
namespace Embeddium::XmppServer {
void OnSession(WebSocketSession::Ptr session);
}
namespace Embeddium::MatchmakerServer {
void OnSession(WebSocketSession::Ptr session);
}
#include <boost/asio.hpp>
#include <memory>
#include <thread>
#include <vector>
#include <atomic>
#include <cstring>
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#include <Windows.h>
namespace Embeddium::Initializer {
namespace asio = boost::asio;
namespace {
struct ServerState {
asio::io_context io{4};
Router router;
std::unique_ptr<HttpServer> httpServer;
std::unique_ptr<WebSocketServer> wsServer;
std::vector<std::thread> threads;
std::atomic<bool> running{false};
};
static ServerState* gState = nullptr;
void WsSessionFactory(WebSocketSession::Ptr session) {
std::string_view proto = session->Protocol();
if (proto == "xmpp") {
XmppServer::OnSession(std::move(session));
} else {
MatchmakerServer::OnSession(std::move(session));
}
}
}
void Run() {
// ── Logging ─────────────────────────────────────────────────────────────
{
const char* cmd = GetCommandLineA();
Log::Init(cmd && std::strstr(cmd, "-console") != nullptr);
}
Log::Info("=== Embeddium starting ===");
Log::Info("Waiting 2 s for game modules to settle...");
Sleep(2000);
Log::Info("Wait complete");
Log::Debug("HookManager::Init — enter");
HookManager::Init();
Log::Info("HookManager::Init — ok");
Sleep(50);
Log::Debug("ExitHook::Install — enter");
ExitHook::Install();
Log::Info("ExitHook::Install — ok");
Sleep(50);
Log::Debug("CurlHook::Install — enter");
bool curlOk = CurlHook::Install();
Log::Info(std::string("CurlHook::Install — ") + (curlOk ? "ok" : "FAILED (URLs will not redirect)"));
Sleep(50);
Log::Debug("AssetManager::Init — enter");
AssetManager::Init();
Log::Info("AssetManager::Init — ok");
Log::Debug("ConfigLoader::LoadEmbedded — enter");
Config cfg = ConfigLoader::LoadEmbedded();
Log::Info("Config loaded: displayName=\"" + cfg.displayName
+ "\" httpPort=" + std::to_string(Config::kHttpPort)
+ " wsPort=" + std::to_string(Config::kWsPort));
Log::Debug("AccountState::Init — enter");
AccountState::Init(cfg.displayName, cfg.bUseConfigDisplayName);
Log::Info("AccountState::Init — ok");
Log::Debug("FriendStore::Init — enter");
FriendStore::Init();
Log::Info("FriendStore::Init — ok");
Log::Debug("PrivacyStore::Init — enter");
PrivacyStore::Init();
Log::Info("PrivacyStore::Init — ok");
Log::Debug("ProfileStore::Init — enter");
ProfileStore::Init(2, 2.0f);
Log::Info("ProfileStore::Init — ok");
Log::Debug("ItemShopService::Init — enter");
ItemShopService::Init();
Log::Info("ItemShopService::Init — ok");
Log::Debug("ServerState — allocating");
gState = new ServerState();
Log::Info("ServerState — ok");
Log::Debug("Routes::RegisterAll — enter");
Routes::RegisterAll(gState->router);
Log::Info("Routes::RegisterAll — ok (" + std::to_string(gState->router.RouteCount()) + " routes)");
Log::Debug("HttpServer — creating on port " + std::to_string(Config::kHttpPort));
gState->httpServer = std::make_unique<HttpServer>(
gState->io,
static_cast<unsigned short>(Config::kHttpPort),
gState->router);
Log::Info("HttpServer — created");
Log::Debug("WebSocketServer — creating on port " + std::to_string(Config::kWsPort));
gState->wsServer = std::make_unique<WebSocketServer>(
gState->io,
static_cast<unsigned short>(Config::kWsPort),
WsSessionFactory);
Log::Info("WebSocketServer — created");
Log::Debug("HttpServer::Start — enter");
gState->httpServer->Start();
Log::Info("HttpServer::Start — ok (port " + std::to_string(Config::kHttpPort) + ")");
Log::Debug("WebSocketServer::Start — enter");
gState->wsServer->Start();
Log::Info("WebSocketServer::Start — ok (port " + std::to_string(Config::kWsPort) + ")");
gState->running = true;
for (int i = 0; i < 4; ++i) {
Log::Debug("Spawning io thread " + std::to_string(i));
gState->threads.emplace_back([&io = gState->io, i]() {
__try {
io.run();
} __except (EXCEPTION_EXECUTE_HANDLER) {
DWORD code = GetExceptionCode();
char msg[80];
wsprintfA(msg, "io thread %d crashed: 0x%08X", i, (unsigned)code);
Log::Error(msg);
}
});
}
Log::Info("=== Embeddium ready — io_context running on 4 threads ===");
}
void Shutdown() {
if (!gState) return;
Log::Info("=== Embeddium shutting down ===");
Log::Debug("WebSocketServer::Stop — enter");
if (gState->wsServer) gState->wsServer->Stop();
Log::Info("WebSocketServer::Stop — ok");
Log::Debug("HttpServer::Stop — enter");
if (gState->httpServer) gState->httpServer->Stop();
Log::Info("HttpServer::Stop — ok");
Log::Debug("io_context::stop — enter");
gState->io.stop();
Log::Info("io_context::stop — ok");
Log::Debug("Joining io threads...");
for (auto& t : gState->threads)
if (t.joinable()) t.join();
Log::Info("All io threads joined");
Log::Debug("HookManager::Shutdown — enter");
HookManager::Shutdown();
Log::Info("HookManager::Shutdown — ok");
delete gState;
gState = nullptr;
Log::Info("=== Shutdown complete ===");
Log::Shutdown();
}
} // namespace Embeddium::Initializer
+12
View File
@@ -0,0 +1,12 @@
#pragma once
namespace Embeddium::Initializer {
// Called from DllMain on a dedicated thread.
// Starts io_context, HTTP + WebSocket servers, installs hooks.
void Run();
// Called on DLL_PROCESS_DETACH — shuts down servers and hooks cleanly.
void Shutdown();
} // namespace Embeddium::Initializer
+125
View File
@@ -0,0 +1,125 @@
#include "Log.h"
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#include <Windows.h>
#include <intrin.h>
// ─────────────────────────────────────────────────────────────────────────────
// All state is POD / zero-initialized — no C++ static constructors.
// Synchronization uses a pure-CPU spinlock (InterlockedCmpXchg + YieldProcessor)
// so there are zero OS/kernel calls involved. This matters because Unreal's
// fiber scheduler cannot tolerate CRITICAL_SECTION / Win32 mutex objects on
// its own threads; those eventually call NtWaitForSingleObject which confuses
// the fiber context. A spinlock is just a 'lock cmpxchg' instruction — fully
// transparent to UE.
// ─────────────────────────────────────────────────────────────────────────────
namespace Embeddium::Log {
static HANDLE sLogFile = INVALID_HANDLE_VALUE;
static volatile LONG sSpinlock = 0;
static bool sHasConsole = false;
static void SpinlockAcquire() {
while (InterlockedCompareExchange(&sSpinlock, 1, 0) != 0)
YieldProcessor(); // 'pause' hint — pure CPU, no kernel call
}
static void SpinlockRelease() {
InterlockedExchange(&sSpinlock, 0);
}
// ── Internal write ───────────────────────────────────────────────────────────
static void Write(const char* level, WORD color, const char* msg, DWORD msgLen) {
SYSTEMTIME st{};
GetLocalTime(&st);
char ts[10]{};
wsprintfA(ts, "%02d:%02d:%02d", st.wHour, st.wMinute, st.wSecond);
// Build line into a fixed stack buffer — no heap allocation.
// Format: "[HH:MM:SS][LEVEL] <msg>\r\n"
const DWORD kOverhead = 20;
DWORD lineLen = msgLen + kOverhead;
char stackBuf[512];
char* line = (lineLen <= sizeof(stackBuf))
? stackBuf
: static_cast<char*>(HeapAlloc(GetProcessHeap(), 0, lineLen));
if (!line) return;
int hdr = wsprintfA(line, "[%s][%s] ", ts, level);
for (DWORD i = 0; i < msgLen; ++i) line[hdr + i] = msg[i];
int total = hdr + (int)msgLen;
line[total++] = '\r';
line[total++] = '\n';
line[total] = '\0';
OutputDebugStringA(line);
SpinlockAcquire();
if (sLogFile != INVALID_HANDLE_VALUE) {
DWORD wr;
WriteFile(sLogFile, line, (DWORD)total, &wr, nullptr);
}
if (sHasConsole) {
HANDLE con = GetStdHandle(STD_OUTPUT_HANDLE);
SetConsoleTextAttribute(con, color);
DWORD wr;
WriteConsoleA(con, line, (DWORD)total, &wr, nullptr);
SetConsoleTextAttribute(con, FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE);
}
SpinlockRelease();
if (line != stackBuf) HeapFree(GetProcessHeap(), 0, line);
}
static void Dispatch(const char* level, WORD color, std::string_view msg) {
Write(level, color, msg.data(), static_cast<DWORD>(msg.size()));
}
// ── Public API ───────────────────────────────────────────────────────────────
void Init(bool allocConsole) {
char path[MAX_PATH]{};
GetModuleFileNameA(nullptr, path, MAX_PATH);
for (int i = lstrlenA(path) - 1; i >= 0; --i) {
if (path[i] == '\\' || path[i] == '/') { path[i + 1] = '\0'; break; }
}
lstrcatA(path, "Embeddium.log");
sLogFile = CreateFileA(path, GENERIC_WRITE, FILE_SHARE_READ, nullptr,
CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr);
if (allocConsole) {
AllocConsole();
FILE* f = nullptr;
freopen_s(&f, "CONOUT$", "w", stdout);
freopen_s(&f, "CONOUT$", "w", stderr);
SetConsoleTitleA("Embeddium");
sHasConsole = true;
}
Info("Embeddium log started");
}
void Shutdown() {
Info("Log closing");
SpinlockAcquire();
if (sLogFile != INVALID_HANDLE_VALUE) {
CloseHandle(sLogFile);
sLogFile = INVALID_HANDLE_VALUE;
}
SpinlockRelease();
}
void Info (std::string_view msg) { Dispatch("INFO ", FOREGROUND_GREEN | FOREGROUND_INTENSITY, msg); }
void Warn (std::string_view msg) { Dispatch("WARN ", FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_INTENSITY, msg); }
void Error(std::string_view msg) { Dispatch("ERROR", FOREGROUND_RED | FOREGROUND_INTENSITY, msg); }
void Debug(std::string_view msg) { Dispatch("DEBUG", FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_INTENSITY, msg); }
} // namespace Embeddium::Log
+20
View File
@@ -0,0 +1,20 @@
#pragma once
#include <string_view>
namespace Embeddium::Log {
// Call once at startup. If allocConsole is true, opens a Windows console
// window for the process (used when the game is launched with -console).
// Always writes to Embeddium.log alongside the host executable.
// OutputDebugStringA output is always emitted regardless of allocConsole.
void Init(bool allocConsole);
// Flush and close the log file. Call from Initializer::Shutdown().
void Shutdown();
void Info (std::string_view msg);
void Warn (std::string_view msg);
void Error(std::string_view msg);
void Debug(std::string_view msg);
} // namespace Embeddium::Log
+212
View File
@@ -0,0 +1,212 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<VCProjectVersion>17.0</VCProjectVersion>
<ProjectGuid>{A1B2C3D4-E5F6-7890-ABCD-EF1234567890}</ProjectGuid>
<Keyword>DynamicLibrary</Keyword>
<RootNamespace>Embeddium</RootNamespace>
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<!-- ═══════════════════════════════════════════════
All dependencies are vendored in vendor/:
Boost (Asio + Beast): vendor/boost
MinHook: vendor/MinHook
nlohmann/json: vendor/nlohmann
pugixml: vendor/pugixml
cURL (import header): vendor/cURL
No external tools (vcpkg, BOOST_ROOT, etc.) required.
═══════════════════════════════════════════════ -->
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<OutDir>$(SolutionDir)bin\Debug\</OutDir>
<IntDir>$(SolutionDir)obj\Debug\$(ProjectName)\</IntDir>
<TargetName>Embeddium</TargetName>
<TargetExt>.dll</TargetExt>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<OutDir>$(SolutionDir)bin\Release\</OutDir>
<IntDir>$(SolutionDir)obj\Release\$(ProjectName)\</IntDir>
<TargetName>Embeddium</TargetName>
<TargetExt>.dll</TargetExt>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>WIN32;_DEBUG;EMBEDDIUM_DLL;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<LanguageStandard>stdcpp20</LanguageStandard>
<AdditionalIncludeDirectories>$(ProjectDir);$(ProjectDir)vendor;$(ProjectDir)vendor\boost;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<MultiProcessorCompilation>true</MultiProcessorCompilation>
<RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
<Optimization>Disabled</Optimization>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<AdditionalDependencies>$(ProjectDir)vendor\MinHook\minhook.x64.lib;Bcrypt.lib;ws2_32.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>WIN32;NDEBUG;EMBEDDIUM_DLL;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<LanguageStandard>stdcpp20</LanguageStandard>
<AdditionalIncludeDirectories>$(ProjectDir);$(ProjectDir)vendor;$(ProjectDir)vendor\boost;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<MultiProcessorCompilation>true</MultiProcessorCompilation>
<RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<WholeProgramOptimization>true</WholeProgramOptimization>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
<LinkTimeCodeGeneration>UseLinkTimeCodeGeneration</LinkTimeCodeGeneration>
<AdditionalDependencies>$(ProjectDir)vendor\MinHook\minhook.x64.lib;Bcrypt.lib;ws2_32.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>
<!-- ═══ SOURCE FILES ═══ -->
<ItemGroup>
<!-- Core -->
<ClCompile Include="Core\DllMain.cpp" />
<ClCompile Include="Core\Initializer.cpp" />
<ClCompile Include="Core\Config.cpp" />
<ClCompile Include="Core\Log.cpp" />
<!-- Hooks -->
<ClCompile Include="Hooks\HookManager.cpp" />
<ClCompile Include="Hooks\CurlHook.cpp" />
<ClCompile Include="Hooks\ExitHook.cpp" />
<!-- Network -->
<ClCompile Include="Network\UriParser.cpp" />
<ClCompile Include="Network\HttpServer.cpp" />
<ClCompile Include="Network\WebSocketServer.cpp" />
<!-- HTTP -->
<ClCompile Include="HTTP\HttpRequest.cpp" />
<ClCompile Include="HTTP\HttpResponse.cpp" />
<ClCompile Include="HTTP\Router.cpp" />
<!-- Routing -->
<ClCompile Include="Routing\Routes.cpp" />
<ClCompile Include="Routing\McpRoutes.cpp" />
<!-- Backend -->
<ClCompile Include="Backend\AccountState.cpp" />
<ClCompile Include="Backend\FriendStore.cpp" />
<ClCompile Include="Backend\PrivacyStore.cpp" />
<ClCompile Include="Backend\ProfileStore.cpp" />
<ClCompile Include="Backend\VersionService.cpp" />
<ClCompile Include="Backend\ItemShopService.cpp" />
<ClCompile Include="Backend\ContentService.cpp" />
<ClCompile Include="Backend\TimelineService.cpp" />
<ClCompile Include="Backend\TheaterService.cpp" />
<ClCompile Include="Backend\CloudStorageService.cpp" />
<ClCompile Include="Backend\MatchmakerServer.cpp" />
<!-- XMPP -->
<ClCompile Include="XMPP\XmppServer.cpp" />
<!-- Assets -->
<ClCompile Include="Assets\AssetManager.cpp" />
<ClCompile Include="Assets\EmbeddedAssets.cpp" />
<!-- Utilities -->
<ClCompile Include="Utilities\StringUtils.cpp" />
<ClCompile Include="Utilities\UuidGenerator.cpp" />
<ClCompile Include="Utilities\Base64.cpp" />
<ClCompile Include="Utilities\HashUtils.cpp" />
<!-- vendor/pugixml -->
<ClCompile Include="vendor\pugixml\pugixml.cpp" />
</ItemGroup>
<!-- ═══ HEADER FILES ═══ -->
<ItemGroup>
<ClInclude Include="Core\Initializer.h" />
<ClInclude Include="Core\Config.h" />
<ClInclude Include="Core\Log.h" />
<ClInclude Include="Hooks\HookManager.h" />
<ClInclude Include="Hooks\CurlHook.h" />
<ClInclude Include="Hooks\ExitHook.h" />
<ClInclude Include="Network\UriParser.h" />
<ClInclude Include="Network\HttpServer.h" />
<ClInclude Include="Network\WebSocketServer.h" />
<ClInclude Include="HTTP\HttpRequest.h" />
<ClInclude Include="HTTP\HttpResponse.h" />
<ClInclude Include="HTTP\Router.h" />
<ClInclude Include="Routing\Routes.h" />
<ClInclude Include="Routing\McpRoutes.h" />
<ClInclude Include="Backend\AccountState.h" />
<ClInclude Include="Backend\FriendStore.h" />
<ClInclude Include="Backend\PrivacyStore.h" />
<ClInclude Include="Backend\ProfileStore.h" />
<ClInclude Include="Backend\VersionService.h" />
<ClInclude Include="Backend\ItemShopService.h" />
<ClInclude Include="Backend\ContentService.h" />
<ClInclude Include="Backend\TimelineService.h" />
<ClInclude Include="Backend\TheaterService.h" />
<ClInclude Include="Backend\CloudStorageService.h" />
<ClInclude Include="Assets\AssetManager.h" />
<ClInclude Include="Assets\EmbeddedAssets.h" />
<ClInclude Include="Utilities\StringUtils.h" />
<ClInclude Include="Utilities\UuidGenerator.h" />
<ClInclude Include="Utilities\Base64.h" />
<ClInclude Include="Utilities\HashUtils.h" />
<ClInclude Include="Utilities\JsonUtils.h" />
<!-- vendor headers (shown in Solution Explorer, not compiled) -->
<ClInclude Include="vendor\cURL\curl.h" />
<ClInclude Include="vendor\cURL\curlhooks.h" />
<ClInclude Include="vendor\cURL\curlver.h" />
<ClInclude Include="vendor\cURL\system.h" />
<ClInclude Include="vendor\MinHook\MinHook.h" />
<ClInclude Include="vendor\nlohmann\json.hpp" />
<ClInclude Include="vendor\pugixml\pugixml.hpp" />
<ClInclude Include="vendor\pugixml\pugiconfig.hpp" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
</Project>
+276
View File
@@ -0,0 +1,276 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<!-- ═══ FILTER DEFINITIONS ═══ -->
<ItemGroup>
<Filter Include="Assets">
<UniqueIdentifier>{11111111-0001-0000-0000-000000000000}</UniqueIdentifier>
</Filter>
<Filter Include="Backend">
<UniqueIdentifier>{11111111-0002-0000-0000-000000000000}</UniqueIdentifier>
</Filter>
<Filter Include="Core">
<UniqueIdentifier>{11111111-0003-0000-0000-000000000000}</UniqueIdentifier>
</Filter>
<Filter Include="Hooks">
<UniqueIdentifier>{11111111-0004-0000-0000-000000000000}</UniqueIdentifier>
</Filter>
<Filter Include="HTTP">
<UniqueIdentifier>{11111111-0005-0000-0000-000000000000}</UniqueIdentifier>
</Filter>
<Filter Include="Network">
<UniqueIdentifier>{11111111-0006-0000-0000-000000000000}</UniqueIdentifier>
</Filter>
<Filter Include="Routing">
<UniqueIdentifier>{11111111-0007-0000-0000-000000000000}</UniqueIdentifier>
</Filter>
<Filter Include="Utilities">
<UniqueIdentifier>{11111111-0008-0000-0000-000000000000}</UniqueIdentifier>
</Filter>
<Filter Include="XMPP">
<UniqueIdentifier>{11111111-0009-0000-0000-000000000000}</UniqueIdentifier>
</Filter>
<Filter Include="vendor">
<UniqueIdentifier>{11111111-000A-0000-0000-000000000000}</UniqueIdentifier>
</Filter>
<Filter Include="vendor\boost">
<UniqueIdentifier>{11111111-000F-0000-0000-000000000000}</UniqueIdentifier>
</Filter>
<Filter Include="vendor\cURL">
<UniqueIdentifier>{11111111-000B-0000-0000-000000000000}</UniqueIdentifier>
</Filter>
<Filter Include="vendor\MinHook">
<UniqueIdentifier>{11111111-000C-0000-0000-000000000000}</UniqueIdentifier>
</Filter>
<Filter Include="vendor\nlohmann">
<UniqueIdentifier>{11111111-000D-0000-0000-000000000000}</UniqueIdentifier>
</Filter>
<Filter Include="vendor\pugixml">
<UniqueIdentifier>{11111111-000E-0000-0000-000000000000}</UniqueIdentifier>
</Filter>
</ItemGroup>
<!-- ═══ SOURCE FILES ═══ -->
<ItemGroup>
<ClCompile Include="Core\DllMain.cpp">
<Filter>Core</Filter>
</ClCompile>
<ClCompile Include="Core\Initializer.cpp">
<Filter>Core</Filter>
</ClCompile>
<ClCompile Include="Core\Config.cpp">
<Filter>Core</Filter>
</ClCompile>
<ClCompile Include="Hooks\HookManager.cpp">
<Filter>Hooks</Filter>
</ClCompile>
<ClCompile Include="Hooks\CurlHook.cpp">
<Filter>Hooks</Filter>
</ClCompile>
<ClCompile Include="Hooks\ExitHook.cpp">
<Filter>Hooks</Filter>
</ClCompile>
<ClCompile Include="Network\UriParser.cpp">
<Filter>Network</Filter>
</ClCompile>
<ClCompile Include="Network\HttpServer.cpp">
<Filter>Network</Filter>
</ClCompile>
<ClCompile Include="Network\WebSocketServer.cpp">
<Filter>Network</Filter>
</ClCompile>
<ClCompile Include="HTTP\HttpRequest.cpp">
<Filter>HTTP</Filter>
</ClCompile>
<ClCompile Include="HTTP\HttpResponse.cpp">
<Filter>HTTP</Filter>
</ClCompile>
<ClCompile Include="HTTP\Router.cpp">
<Filter>HTTP</Filter>
</ClCompile>
<ClCompile Include="Routing\Routes.cpp">
<Filter>Routing</Filter>
</ClCompile>
<ClCompile Include="Routing\McpRoutes.cpp">
<Filter>Routing</Filter>
</ClCompile>
<ClCompile Include="Backend\AccountState.cpp">
<Filter>Backend</Filter>
</ClCompile>
<ClCompile Include="Backend\FriendStore.cpp">
<Filter>Backend</Filter>
</ClCompile>
<ClCompile Include="Backend\PrivacyStore.cpp">
<Filter>Backend</Filter>
</ClCompile>
<ClCompile Include="Backend\ProfileStore.cpp">
<Filter>Backend</Filter>
</ClCompile>
<ClCompile Include="Backend\VersionService.cpp">
<Filter>Backend</Filter>
</ClCompile>
<ClCompile Include="Backend\ItemShopService.cpp">
<Filter>Backend</Filter>
</ClCompile>
<ClCompile Include="Backend\ContentService.cpp">
<Filter>Backend</Filter>
</ClCompile>
<ClCompile Include="Backend\TimelineService.cpp">
<Filter>Backend</Filter>
</ClCompile>
<ClCompile Include="Backend\TheaterService.cpp">
<Filter>Backend</Filter>
</ClCompile>
<ClCompile Include="Backend\CloudStorageService.cpp">
<Filter>Backend</Filter>
</ClCompile>
<ClCompile Include="Backend\MatchmakerServer.cpp">
<Filter>Backend</Filter>
</ClCompile>
<ClCompile Include="XMPP\XmppServer.cpp">
<Filter>XMPP</Filter>
</ClCompile>
<ClCompile Include="Assets\AssetManager.cpp">
<Filter>Assets</Filter>
</ClCompile>
<ClCompile Include="Assets\EmbeddedAssets.cpp">
<Filter>Assets</Filter>
</ClCompile>
<ClCompile Include="Utilities\StringUtils.cpp">
<Filter>Utilities</Filter>
</ClCompile>
<ClCompile Include="Utilities\UuidGenerator.cpp">
<Filter>Utilities</Filter>
</ClCompile>
<ClCompile Include="Utilities\Base64.cpp">
<Filter>Utilities</Filter>
</ClCompile>
<ClCompile Include="Utilities\HashUtils.cpp">
<Filter>Utilities</Filter>
</ClCompile>
<ClCompile Include="vendor\pugixml\pugixml.cpp">
<Filter>vendor\pugixml</Filter>
</ClCompile>
<ClCompile Include="Core\Log.cpp">
<Filter>Core</Filter>
</ClCompile>
</ItemGroup>
<!-- ═══ HEADER FILES ═══ -->
<ItemGroup>
<ClInclude Include="Core\Initializer.h">
<Filter>Core</Filter>
</ClInclude>
<ClInclude Include="Core\Config.h">
<Filter>Core</Filter>
</ClInclude>
<ClInclude Include="Hooks\HookManager.h">
<Filter>Hooks</Filter>
</ClInclude>
<ClInclude Include="Hooks\CurlHook.h">
<Filter>Hooks</Filter>
</ClInclude>
<ClInclude Include="Hooks\ExitHook.h">
<Filter>Hooks</Filter>
</ClInclude>
<ClInclude Include="Network\UriParser.h">
<Filter>Network</Filter>
</ClInclude>
<ClInclude Include="Network\HttpServer.h">
<Filter>Network</Filter>
</ClInclude>
<ClInclude Include="Network\WebSocketServer.h">
<Filter>Network</Filter>
</ClInclude>
<ClInclude Include="HTTP\HttpRequest.h">
<Filter>HTTP</Filter>
</ClInclude>
<ClInclude Include="HTTP\HttpResponse.h">
<Filter>HTTP</Filter>
</ClInclude>
<ClInclude Include="HTTP\Router.h">
<Filter>HTTP</Filter>
</ClInclude>
<ClInclude Include="Routing\Routes.h">
<Filter>Routing</Filter>
</ClInclude>
<ClInclude Include="Routing\McpRoutes.h">
<Filter>Routing</Filter>
</ClInclude>
<ClInclude Include="Backend\AccountState.h">
<Filter>Backend</Filter>
</ClInclude>
<ClInclude Include="Backend\FriendStore.h">
<Filter>Backend</Filter>
</ClInclude>
<ClInclude Include="Backend\PrivacyStore.h">
<Filter>Backend</Filter>
</ClInclude>
<ClInclude Include="Backend\ProfileStore.h">
<Filter>Backend</Filter>
</ClInclude>
<ClInclude Include="Backend\VersionService.h">
<Filter>Backend</Filter>
</ClInclude>
<ClInclude Include="Backend\ItemShopService.h">
<Filter>Backend</Filter>
</ClInclude>
<ClInclude Include="Backend\ContentService.h">
<Filter>Backend</Filter>
</ClInclude>
<ClInclude Include="Backend\TimelineService.h">
<Filter>Backend</Filter>
</ClInclude>
<ClInclude Include="Backend\TheaterService.h">
<Filter>Backend</Filter>
</ClInclude>
<ClInclude Include="Backend\CloudStorageService.h">
<Filter>Backend</Filter>
</ClInclude>
<ClInclude Include="Assets\AssetManager.h">
<Filter>Assets</Filter>
</ClInclude>
<ClInclude Include="Assets\EmbeddedAssets.h">
<Filter>Assets</Filter>
</ClInclude>
<ClInclude Include="Utilities\StringUtils.h">
<Filter>Utilities</Filter>
</ClInclude>
<ClInclude Include="Utilities\UuidGenerator.h">
<Filter>Utilities</Filter>
</ClInclude>
<ClInclude Include="Utilities\Base64.h">
<Filter>Utilities</Filter>
</ClInclude>
<ClInclude Include="Utilities\HashUtils.h">
<Filter>Utilities</Filter>
</ClInclude>
<ClInclude Include="Utilities\JsonUtils.h">
<Filter>Utilities</Filter>
</ClInclude>
<ClInclude Include="vendor\cURL\curl.h">
<Filter>vendor\cURL</Filter>
</ClInclude>
<ClInclude Include="vendor\cURL\curlhooks.h">
<Filter>vendor\cURL</Filter>
</ClInclude>
<ClInclude Include="vendor\cURL\curlver.h">
<Filter>vendor\cURL</Filter>
</ClInclude>
<ClInclude Include="vendor\cURL\system.h">
<Filter>vendor\cURL</Filter>
</ClInclude>
<ClInclude Include="vendor\MinHook\MinHook.h">
<Filter>vendor\MinHook</Filter>
</ClInclude>
<ClInclude Include="vendor\nlohmann\json.hpp">
<Filter>vendor\nlohmann</Filter>
</ClInclude>
<ClInclude Include="vendor\pugixml\pugixml.hpp">
<Filter>vendor\pugixml</Filter>
</ClInclude>
<ClInclude Include="vendor\pugixml\pugiconfig.hpp">
<Filter>vendor\pugixml</Filter>
</ClInclude>
<ClInclude Include="Core\Log.h">
<Filter>Core</Filter>
</ClInclude>
</ItemGroup>
</Project>
+86
View File
@@ -0,0 +1,86 @@
#include "HttpRequest.h"
#include "../Utilities/StringUtils.h"
#include "../Utilities/JsonUtils.h"
namespace Embeddium {
HttpRequest::HttpRequest(BeastRequest req)
: raw_(std::move(req))
, query_("")
{
// Extract method
method_ = std::string(http::to_string(raw_.method()));
// Decompose target into path + query
std::string_view target(raw_.target());
size_t qpos = target.find('?');
if (qpos != std::string_view::npos) {
path_ = std::string(target.substr(0, qpos));
queryString_ = std::string(target.substr(qpos));
} else {
path_ = std::string(target);
queryString_ = "";
}
// Reconstruct QueryParams
query_ = QueryParams(queryString_);
// Body
body_ = raw_.body();
}
std::string HttpRequest::Header(std::string_view name) const {
// Beast stores header names case-insensitively via their enum values.
// For unknown names we must iterate.
std::string lower = StringUtils::ToLower(name);
for (auto const& field : raw_) {
if (StringUtils::IEquals(field.name_string(), lower)) {
return std::string(field.value());
}
}
return "";
}
void HttpRequest::ParseCookies() const {
if (cookies_.has_value()) return;
cookies_.emplace();
std::string cookieHeader = Header("cookie");
if (cookieHeader.empty()) return;
// Format: name=value; name2=value2
auto pairs = StringUtils::Split(cookieHeader, ';');
for (auto& pair : pairs) {
pair = StringUtils::Trim(pair);
size_t eq = pair.find('=');
if (eq != std::string::npos) {
std::string key = StringUtils::Trim(pair.substr(0, eq));
std::string value = StringUtils::Trim(pair.substr(eq + 1));
(*cookies_)[key] = value;
}
}
}
std::string HttpRequest::Cookie(std::string_view name) const {
ParseCookies();
auto it = cookies_->find(std::string(name));
if (it == cookies_->end()) return "";
return it->second;
}
Json HttpRequest::BodyJson() const {
if (body_.empty()) return Json::object();
return JsonUtils::ParseSafe(body_);
}
std::string HttpRequest::Query(std::string_view key, std::string_view def) const {
return query_.Get(key, def);
}
std::string HttpRequest::Param(std::string_view name, std::string_view def) const {
auto it = params_.find(std::string(name));
if (it == params_.end()) return std::string(def);
return it->second;
}
} // namespace Embeddium
+97
View File
@@ -0,0 +1,97 @@
#pragma once
// Boost.Beast / Asio
#define BOOST_BEAST_USE_STD_STRING_VIEW
#include <boost/beast/http.hpp>
#include <boost/beast/core.hpp>
#include <string>
#include <string_view>
#include <unordered_map>
#include <optional>
#include "../Network/UriParser.h"
#include "../Utilities/JsonUtils.h"
namespace Embeddium {
namespace beast = boost::beast;
namespace http = beast::http;
// ─────────────────────────────────────────────────────────────────────────────
// HttpRequest
// Wraps a Boost.Beast HTTP request and provides ergonomic accessors
// matching the patterns used throughout the route handlers.
//
// Immutable after construction.
// ─────────────────────────────────────────────────────────────────────────────
class HttpRequest {
public:
using BeastRequest = http::request<http::string_body>;
explicit HttpRequest(BeastRequest req);
// HTTP method as string: "GET", "POST", "PUT", "DELETE", "PATCH", "ALL"
std::string_view Method() const { return method_; }
// Full raw path, e.g. "/fortnite/api/game/v2/profile/abc/client/QueryProfile"
std::string_view Path() const { return path_; }
// Raw query string including leading '?', e.g. "?profileId=athena&rvn=5"
std::string_view QueryString() const { return queryString_; }
// Parsed query parameters — full map
const QueryParams& Query() const { return query_; }
// Lookup a single query parameter by name. Returns def ("") if absent.
std::string Query(std::string_view key, std::string_view def = "") const;
// Header value by name (case-insensitive). Returns "" if absent.
std::string Header(std::string_view name) const;
// User-Agent header shorthand
std::string UserAgent() const { return Header("user-agent"); }
// Accept-Language header shorthand
std::string AcceptLanguage() const { return Header("accept-language"); }
// Cookie value by name. Returns "" if absent.
std::string Cookie(std::string_view name) const;
// Raw request body
std::string_view Body() const { return body_; }
// Parse body as JSON. Returns empty object on failure or empty body.
Json BodyJson() const;
// Route parameters extracted by the Router (e.g. :accountId → value).
// Set by Router before calling the handler.
const std::unordered_map<std::string, std::string>& Params() const {
return params_;
}
std::string Param(std::string_view name, std::string_view def = "") const;
// Allows Router to inject matched path parameters
void SetParams(std::unordered_map<std::string, std::string> p) {
params_ = std::move(p);
}
// Original Beast request (for low-level access if needed)
const BeastRequest& Raw() const { return raw_; }
private:
BeastRequest raw_;
std::string method_;
std::string path_;
std::string queryString_;
QueryParams query_;
std::string body_;
// Parsed cookies (lazy — built on first Cookie() call)
mutable std::optional<std::unordered_map<std::string,std::string>> cookies_;
std::unordered_map<std::string, std::string> params_;
void ParseCookies() const;
};
} // namespace Embeddium
+125
View File
@@ -0,0 +1,125 @@
#include "HttpResponse.h"
namespace Embeddium {
HttpResponse::HttpResponse() {
resp_.version(11); // HTTP/1.1
resp_.keep_alive(false);
resp_.set(http::field::server, "Embeddium/1.0");
}
HttpResponse& HttpResponse::Status(unsigned code) {
status_ = code;
return *this;
}
HttpResponse& HttpResponse::ContentType(std::string_view type) {
resp_.set(http::field::content_type, type);
return *this;
}
HttpResponse& HttpResponse::Json(const Embeddium::Json& j) {
body_ = j.dump();
resp_.set(http::field::content_type, "application/json");
return *this;
}
HttpResponse& HttpResponse::Json(std::string_view jsonStr) {
body_ = std::string(jsonStr);
resp_.set(http::field::content_type, "application/json");
return *this;
}
HttpResponse& HttpResponse::Text(std::string_view text) {
body_ = std::string(text);
resp_.set(http::field::content_type, "text/plain");
return *this;
}
HttpResponse& HttpResponse::Binary(std::span<const uint8_t> data,
std::string_view contentType) {
body_.assign(reinterpret_cast<const char*>(data.data()), data.size());
resp_.set(http::field::content_type, contentType);
return *this;
}
HttpResponse& HttpResponse::Binary(std::string_view data,
std::string_view contentType) {
body_ = std::string(data);
resp_.set(http::field::content_type, contentType);
return *this;
}
HttpResponse& HttpResponse::NoContent() {
status_ = 204;
body_.clear();
return *this;
}
HttpResponse& HttpResponse::SetHeader(std::string_view name, std::string_view value) {
resp_.set(std::string(name), value);
return *this;
}
HttpResponse& HttpResponse::SetCookie(std::string_view name, std::string_view value,
std::string_view path) {
std::string cookie(name);
cookie += "=";
cookie += value;
cookie += "; Path=";
cookie += path;
resp_.set(http::field::set_cookie, cookie);
return *this;
}
// ── Static factory ────────────────────────────────────────────────────────────
HttpResponse HttpResponse::NotFound(std::string_view errorCode, int numericCode) {
HttpResponse r;
r.status_ = 404;
Embeddium::Json j;
j["errorCode"] = std::string(errorCode);
j["errorMessage"] = "Sorry the resource you were trying to find could not be found";
j["numericErrorCode"] = numericCode;
j["originatingService"] = "any";
j["intent"] = "prod";
r.body_ = j.dump();
r.resp_.set(http::field::content_type, "application/json");
r.resp_.set("X-Epic-Error-Name", errorCode);
r.resp_.set("X-Epic-Error-Code", std::to_string(numericCode));
return r;
}
HttpResponse HttpResponse::EpicError(unsigned httpStatus,
std::string_view errorCode,
std::string_view message,
int numericCode) {
HttpResponse r;
r.status_ = httpStatus;
Embeddium::Json j;
j["errorCode"] = std::string(errorCode);
j["errorMessage"] = std::string(message);
j["numericErrorCode"] = numericCode;
j["originatingService"] = "any";
j["intent"] = "prod";
r.body_ = j.dump();
r.resp_.set(http::field::content_type, "application/json");
r.resp_.set("X-Epic-Error-Name", errorCode);
r.resp_.set("X-Epic-Error-Code", std::to_string(numericCode));
return r;
}
// ── Build (move) ──────────────────────────────────────────────────────────────
HttpResponse::BeastResponse HttpResponse::Build() {
resp_.result(status_);
resp_.body() = std::move(body_);
resp_.prepare_payload(); // sets Content-Length
return std::move(resp_);
}
} // namespace Embeddium
+78
View File
@@ -0,0 +1,78 @@
#pragma once
#define BOOST_BEAST_USE_STD_STRING_VIEW
#include <boost/beast/http.hpp>
#include <boost/beast/core.hpp>
#include <string>
#include <string_view>
#include <span>
#include <cstdint>
#include "../Utilities/JsonUtils.h"
namespace Embeddium {
namespace beast = boost::beast;
namespace http = beast::http;
// ─────────────────────────────────────────────────────────────────────────────
// HttpResponse
// Builder for HTTP responses. All methods return *this for chaining.
// Designed to be constructed per-request and consumed exactly once.
// ─────────────────────────────────────────────────────────────────────────────
class HttpResponse {
public:
using BeastResponse = http::response<http::string_body>;
HttpResponse();
// ── Status ────────────────────────────────────────────────────────────
HttpResponse& Status(unsigned code);
// ── Content-Type helpers ──────────────────────────────────────────────
// Send a JSON body (Content-Type: application/json)
HttpResponse& Json(const Embeddium::Json& j);
HttpResponse& Json(std::string_view jsonStr);
// Send a plain-text body
HttpResponse& Text(std::string_view text);
// Send a raw binary body
HttpResponse& Binary(std::span<const uint8_t> data,
std::string_view contentType = "application/octet-stream");
HttpResponse& Binary(std::string_view data,
std::string_view contentType = "application/octet-stream");
// Send empty body with 204 No Content
HttpResponse& NoContent();
// ── Header helpers ────────────────────────────────────────────────────
HttpResponse& SetHeader(std::string_view name, std::string_view value);
HttpResponse& SetCookie(std::string_view name, std::string_view value,
std::string_view path = "/");
HttpResponse& ContentType(std::string_view type);
// ── Epic-specific error response ──────────────────────────────────────
static HttpResponse NotFound(std::string_view errorCode =
"errors.com.embeddium.common.not_found",
int numericCode = 1004);
static HttpResponse EpicError(unsigned httpStatus,
std::string_view errorCode,
std::string_view message,
int numericCode);
// ── Serialise to Beast response (moves internal state) ────────────────
BeastResponse Build();
// Check if a body has been set
bool HasBody() const { return !body_.empty() || status_ == 204; }
private:
BeastResponse resp_;
std::string body_;
unsigned status_ = 200;
bool built_ = false;
};
} // namespace Embeddium
+141
View File
@@ -0,0 +1,141 @@
#include "Router.h"
#include "../Core/Log.h"
#include "../Utilities/StringUtils.h"
namespace Embeddium {
std::vector<Router::PatternSegment> Router::ParsePattern(std::string_view pattern) {
std::vector<PatternSegment> segments;
if (!pattern.empty() && pattern[0] == '/') pattern = pattern.substr(1);
auto parts = StringUtils::Split(pattern, '/');
for (auto& part : parts) {
if (part.empty()) continue;
PatternSegment seg;
if (part == "*") {
seg.kind = PatternSegment::Kind::Wildcard;
seg.value = "*";
} else if (!part.empty() && part[0] == ':') {
seg.kind = PatternSegment::Kind::Param;
seg.value = part.substr(1);
} else {
seg.kind = PatternSegment::Kind::Literal;
seg.value = part;
}
segments.push_back(std::move(seg));
}
return segments;
}
void Router::Add(std::string method, std::string pattern, RouteHandler handler) {
Route route;
route.method = StringUtils::ToUpper(method);
route.pattern = pattern;
route.handler = std::move(handler);
// Non-slash-terminated trailing '*' means prefix match (e.g. "/api/versioncheck*")
if (!pattern.empty() && pattern.back() == '*' &&
(pattern.size() < 2 || pattern[pattern.size() - 2] != '/')) {
route.trailingWildcard = true;
pattern = pattern.substr(0, pattern.size() - 1);
}
route.segments = ParsePattern(pattern);
if (!route.segments.empty() &&
route.segments.back().kind == PatternSegment::Kind::Wildcard) {
route.trailingWildcard = true;
}
routes_.push_back(std::move(route));
}
bool Router::Match(const Route& route,
std::string_view path,
std::unordered_map<std::string, std::string>& params) {
params.clear();
std::string_view p = path;
if (!p.empty() && p[0] == '/') p = p.substr(1);
auto pathParts = StringUtils::Split(p, '/');
const auto& segs = route.segments;
if (route.trailingWildcard) {
size_t fixedCount = segs.size();
if (!segs.empty() && segs.back().kind == PatternSegment::Kind::Wildcard)
fixedCount = segs.size() - 1;
if (pathParts.size() < fixedCount) return false;
for (size_t i = 0; i < fixedCount; ++i) {
const auto& seg = segs[i];
if (seg.kind == PatternSegment::Kind::Literal) {
if (!StringUtils::IEquals(pathParts[i], seg.value)) return false;
} else if (seg.kind == PatternSegment::Kind::Param) {
if (pathParts[i].empty()) return false;
params[seg.value] = pathParts[i];
}
}
if (!segs.empty() && segs.back().kind == PatternSegment::Kind::Wildcard) {
std::string rest;
for (size_t i = fixedCount; i < pathParts.size(); ++i) {
if (!rest.empty()) rest += '/';
rest += pathParts[i];
}
params["*"] = std::move(rest);
}
return true;
}
if (pathParts.size() != segs.size()) return false;
for (size_t i = 0; i < segs.size(); ++i) {
const auto& seg = segs[i];
switch (seg.kind) {
case PatternSegment::Kind::Literal:
if (seg.value == "*") {
params["*"] = pathParts[i];
} else if (!StringUtils::IEquals(pathParts[i], seg.value)) {
return false;
}
break;
case PatternSegment::Kind::Param:
if (pathParts[i].empty()) return false;
params[seg.value] = pathParts[i];
break;
case PatternSegment::Kind::Wildcard:
params["*"] = pathParts[i];
break;
}
}
return true;
}
std::optional<HttpResponse> Router::Dispatch(HttpRequest& req) const {
std::string method = StringUtils::ToUpper(std::string(req.Method()));
std::string_view path = req.Path();
Log::Debug(method + " " + std::string(path));
std::unordered_map<std::string, std::string> params;
for (const auto& route : routes_) {
if (route.method != "ALL" && route.method != method) continue;
if (Match(route, path, params)) {
req.SetParams(std::move(params));
return route.handler(req);
}
}
Log::Warn("404 " + method + " " + std::string(path));
return std::nullopt;
}
} // namespace Embeddium
+86
View File
@@ -0,0 +1,86 @@
#pragma once
#include <functional>
#include <string>
#include <string_view>
#include <vector>
#include <unordered_map>
#include <optional>
#include "HttpRequest.h"
#include "HttpResponse.h"
namespace Embeddium {
// ─────────────────────────────────────────────────────────────────────────────
// RouteHandler
// A handler receives a fully-populated HttpRequest (including route params)
// and must return a fully-built HttpResponse.
// ─────────────────────────────────────────────────────────────────────────────
using RouteHandler = std::function<HttpResponse(HttpRequest&)>;
// ─────────────────────────────────────────────────────────────────────────────
// Router
// Pattern-matching HTTP route registry.
//
// /literal/path — exact segment match
// :paramName — captures a single path segment into params
// * — wildcard: matches one or more segments
//
// Registration order matters — first matching route wins.
//
// Method matching:
// "GET" "POST" "PUT" "DELETE" "PATCH" — exact method match
// "ALL" — matches any method
//
// After Init() (registration), all Dispatch() calls are read-only and
// thread-safe.
// ─────────────────────────────────────────────────────────────────────────────
class Router {
public:
// Register a route. Method is case-insensitive.
void Add(std::string method, std::string pattern, RouteHandler handler);
// Convenience wrappers
void Get (std::string pattern, RouteHandler h) { Add("GET", std::move(pattern), std::move(h)); }
void Post (std::string pattern, RouteHandler h) { Add("POST", std::move(pattern), std::move(h)); }
void Put (std::string pattern, RouteHandler h) { Add("PUT", std::move(pattern), std::move(h)); }
void Delete(std::string pattern, RouteHandler h) { Add("DELETE", std::move(pattern), std::move(h)); }
void Patch (std::string pattern, RouteHandler h) { Add("PATCH", std::move(pattern), std::move(h)); }
void All (std::string pattern, RouteHandler h) { Add("ALL", std::move(pattern), std::move(h)); }
// Attempt to dispatch the request. Returns the handler's response on match.
// Returns nullopt if no route matched (caller should send 404).
std::optional<HttpResponse> Dispatch(HttpRequest& req) const;
// Number of registered routes
size_t RouteCount() const { return routes_.size(); }
private:
// ── Internal route entry ──────────────────────────────────────────────
struct PatternSegment {
enum class Kind { Literal, Param, Wildcard };
Kind kind;
std::string value; // literal text or param name
};
struct Route {
std::string method; // uppercase, or "ALL"
std::string pattern; // original for debugging
std::vector<PatternSegment> segments;
bool trailingWildcard = false;
RouteHandler handler;
};
std::vector<Route> routes_;
// Parse a pattern string into segments
static std::vector<PatternSegment> ParsePattern(std::string_view pattern);
// Try to match a path against a route's segments.
// On success, fills params. Returns true.
static bool Match(const Route& route,
std::string_view path,
std::unordered_map<std::string, std::string>& params);
};
} // namespace Embeddium
+245
View File
@@ -0,0 +1,245 @@
#include "CurlHook.h"
#include "HookManager.h"
#include "../vendor/cURL/curl.h"
#include "../Network/UriParser.h"
#include "../Core/Config.h"
#include "../Core/Log.h"
#include "../Utilities/StringUtils.h"
#include <Windows.h>
#include <intrin.h>
#include <cstdarg>
#include <cstdio>
#include <string>
#include <cstdint>
namespace Embeddium::CurlHook {
using FnCurlSetOpt = CURLcode(*)(struct Curl_easy*, CURLoption, va_list);
using FnCurlEasySetOpt = CURLcode(*)(struct Curl_easy*, CURLoption, ...);
static FnCurlSetOpt gCurlSetOpt = nullptr;
static FnCurlEasySetOpt gCurlEasySetOpt = nullptr;
static CURLcode ForwardOpt(struct Curl_easy* data, CURLoption tag, ...) {
va_list arg;
va_start(arg, tag);
CURLcode r;
if (gCurlSetOpt) {
r = gCurlSetOpt(data, tag, arg);
} else {
uintptr_t val = va_arg(arg, uintptr_t);
r = gCurlEasySetOpt(data, tag, val);
}
va_end(arg);
return r;
}
static uintptr_t FindPattern(const char* pattern, const char* mask) {
HMODULE hMod = GetModuleHandleA(nullptr);
if (!hMod) return 0;
auto* dos = reinterpret_cast<IMAGE_DOS_HEADER*>(hMod);
auto* nt = reinterpret_cast<IMAGE_NT_HEADERS*>(
reinterpret_cast<uint8_t*>(hMod) + dos->e_lfanew);
uintptr_t base = reinterpret_cast<uintptr_t>(hMod);
uintptr_t size = nt->OptionalHeader.SizeOfImage;
size_t patLen = strlen(mask);
for (uintptr_t i = 0; i < size - patLen; ++i) {
bool found = true;
for (size_t j = 0; j < patLen && found; ++j) {
if (mask[j] == 'x')
found = reinterpret_cast<uint8_t*>(base + i)[j] == static_cast<uint8_t>(pattern[j]);
}
if (found) return base + i;
}
return 0;
}
static uintptr_t SigScan(const char* sig) {
std::string bytes, mask;
const char* p = sig;
while (*p) {
while (*p == ' ') ++p;
if (!*p) break;
if (*p == '?') {
bytes.push_back(0);
mask.push_back('?');
++p;
if (*p == '?') ++p;
} else {
bytes.push_back(static_cast<char>(static_cast<uint8_t>(strtoul(p, nullptr, 16))));
mask.push_back('x');
p += 2;
}
}
return FindPattern(bytes.data(), mask.c_str());
}
static bool ShouldRedirect(const Uri& uri) {
return StringUtils::EndsWith(uri.Host, "ol.epicgames.com")
|| StringUtils::EndsWith(uri.Host, "epicgames.dev")
|| StringUtils::EndsWith(uri.Host, "ol.epicgames.net")
|| StringUtils::EndsWith(uri.Host, ".akamaized.net")
|| StringUtils::EndsWith(uri.Host, "on.epicgames.com")
|| StringUtils::EndsWith(uri.Host, "game-social.epicgames.com")
|| StringUtils::Contains(uri.Host, "superawesome.com")
|| StringUtils::Contains(uri.Host, "ak.epicgames.com");
}
static CURLcode CurlEasySetOptDetour_Impl(struct Curl_easy* data, CURLoption tag, va_list arg)
{
CURLcode result = CURLE_OK;
if (!data)
return CURLE_BAD_FUNCTION_ARGUMENT;
if (tag == CURLOPT_SSL_VERIFYPEER)
{
Log::Debug("CurlHook: CURLOPT_SSL_VERIFYPEER — disabling");
result = ForwardOpt(data, tag, 0L);
Log::Debug("CurlHook: CURLOPT_SSL_VERIFYPEER — ok");
}
else if (tag == CURLOPT_URL)
{
const char* rawUrl = va_arg(arg, const char*);
std::string url = rawUrl ? rawUrl : "";
Uri uri = Uri::Parse(url);
if (ShouldRedirect(uri))
{
std::string redirected = Uri::CreateUri(
Config::kRedirectProtocol,
Config::kRedirectHost,
Config::kRedirectPort,
uri.Path,
uri.QueryString
);
Log::Debug("CurlHook: " + url + " -> " + redirected);
result = ForwardOpt(data, tag, redirected.c_str());
Log::Debug("CurlHook: CURLOPT_URL forward — ok");
}
else
{
Log::Debug("CurlHook: CURLOPT_URL passthrough — " + url);
result = ForwardOpt(data, tag, url.c_str());
}
}
else
{
char dbg[64];
wsprintfA(dbg, "CurlHook: tag=%d forwarding", (int)tag);
Log::Debug(dbg);
if (gCurlSetOpt)
{
result = gCurlSetOpt(data, tag, arg);
}
else
{
uintptr_t val = va_arg(arg, uintptr_t);
result = gCurlEasySetOpt(data, tag, val);
}
wsprintfA(dbg, "CurlHook: tag=%d ok", (int)tag);
Log::Debug(dbg);
}
return result;
}
static CURLcode CurlEasySetOptDetour(struct Curl_easy* data, CURLoption tag, ...)
{
va_list arg;
va_start(arg, tag);
CURLcode result = CURLE_FAILED_INIT;
__try
{
result = CurlEasySetOptDetour_Impl(data, tag, arg);
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
DWORD code = GetExceptionCode();
char msg[128];
wsprintfA(msg, "CurlHook: CRASH in detour tag=%d code=0x%08X",
(int)tag, (unsigned)code);
Log::Error(msg);
OutputDebugStringA(msg);
}
va_end(arg);
return result;
}
bool Install() {
Log::Debug("CurlHook: scanning for curl_easy_setopt...");
uintptr_t addrEasySetOpt = SigScan(
"89 54 24 10 4C 89 44 24 18 4C 89 4C 24 20 "
"48 83 EC 28 48 85 C9 75 08 8D 41 2B 48 83 C4 28 C3 4C");
for (int attempt = 0; attempt < 10 && !addrEasySetOpt; ++attempt) {
Sleep(300);
addrEasySetOpt = SigScan(
"89 54 24 10 4C 89 44 24 18 4C 89 4C 24 20 "
"48 83 EC 28 48 85 C9 75 08 8D 41 2B 48 83 C4 28 C3 4C");
}
if (!addrEasySetOpt) {
Log::Error("CurlHook: curl_easy_setopt not found — redirects disabled");
return false;
}
char hexBuf[24];
wsprintfA(hexBuf, "0x%llX", (unsigned long long)addrEasySetOpt);
Log::Info(std::string("CurlHook: curl_easy_setopt at ") + hexBuf);
uintptr_t addrSetOpt = SigScan(
"48 89 5C 24 08 48 89 6C 24 10 48 89 74 24 18 57 48 83 EC 30 "
"33 ED 49 8B F0 48 8B D9");
if (!addrSetOpt)
addrSetOpt = SigScan(
"48 89 5C 24 08 48 89 6C 24 10 56 57 41 56 48 83 EC 50 "
"33 ED 49 8B F0 8B DA 48 8B F9");
if (!addrSetOpt)
addrSetOpt = SigScan(
"48 89 5C 24 ? 55 56 57 41 56 41 57 48 83 EC 50 33 DB "
"49 8B F0 48 8B F9 8B EB 81 FA");
if (addrSetOpt) {
wsprintfA(hexBuf, "0x%llX", (unsigned long long)addrSetOpt);
Log::Info(std::string("CurlHook: curl_setopt (va_list) at ") + hexBuf);
} else {
Log::Warn("CurlHook: curl_setopt (va_list) not found — will use trampoline fallback");
}
gCurlSetOpt = reinterpret_cast<FnCurlSetOpt>(addrSetOpt);
gCurlEasySetOpt = reinterpret_cast<FnCurlEasySetOpt>(addrEasySetOpt);
bool ok = HookManager::Install(
reinterpret_cast<void*>(addrEasySetOpt),
reinterpret_cast<void*>(&CurlEasySetOptDetour),
reinterpret_cast<void**>(&gCurlEasySetOpt));
if (ok)
Log::Info("CurlHook: hook installed");
else
Log::Error("CurlHook: MinHook installation failed");
return ok;
}
} // namespace Embeddium::CurlHook
+9
View File
@@ -0,0 +1,9 @@
#pragma once
namespace Embeddium::CurlHook {
// Scan game memory for curl_easy_setopt, install the detour.
// Returns true if the hook was successfully installed.
bool Install();
} // namespace Embeddium::CurlHook
+117
View File
@@ -0,0 +1,117 @@
#include "ExitHook.h"
#include "HookManager.h"
#include "../Core/Log.h"
#include <string>
#include <Windows.h>
#include <cstdint>
#include <cstring>
#include <cstdio>
namespace Embeddium::ExitHook {
static void UnsafeEnvironmentPopupStub(void*, unsigned __int8, __int64, char) {}
static void RequestExitWithStatusStub(bool, unsigned char) {}
// IDA-style "89 54 ? ..." pattern scanner over the main module .text
static uintptr_t SigScan(const char* sig) {
HMODULE hMod = GetModuleHandleA(nullptr);
if (!hMod) return 0;
auto* dos = reinterpret_cast<IMAGE_DOS_HEADER*>(hMod);
auto* nt = reinterpret_cast<IMAGE_NT_HEADERS*>(
reinterpret_cast<uint8_t*>(hMod) + dos->e_lfanew);
uintptr_t base = reinterpret_cast<uintptr_t>(hMod);
uintptr_t size = nt->OptionalHeader.SizeOfImage;
char patBytes[256] = {};
char patMask[256] = {};
size_t len = 0;
const char* p = sig;
while (*p && len < sizeof(patBytes) - 1) {
while (*p == ' ') ++p;
if (!*p) break;
if (*p == '?') {
patBytes[len] = 0; patMask[len] = '?';
++len; ++p;
if (*p == '?') ++p;
} else {
patBytes[len] = static_cast<char>(static_cast<uint8_t>(strtoul(p, nullptr, 16)));
patMask[len] = 'x';
++len; p += 2;
}
}
for (uintptr_t i = 0; i < size - len; ++i) {
bool ok = true;
for (size_t j = 0; j < len && ok; ++j) {
if (patMask[j] == 'x')
ok = reinterpret_cast<uint8_t*>(base + i)[j] == static_cast<uint8_t>(patBytes[j]);
}
if (ok) return base + i;
}
return 0;
}
void Install() {
// UnsafeEnvironmentPopup — multiple signatures across UE versions
uintptr_t addrUEP = SigScan(
"4C 8B DC 55 49 8D AB ? ? ? ? 48 81 EC ? ? ? ? 48 8B 05 ? ? ? ? "
"48 33 C4 48 89 85 ? ? ? ? 49 89 73 F0 49 89 7B E8 48 8B F9 "
"4D 89 63 E0 4D 8B E0 4D 89 6B D8");
if (!addrUEP) {
addrUEP = SigScan(
"4C 8B DC 55 49 8D AB ? ? ? ? 48 81 EC ? ? ? ? 48 8B 05 "
"? ? ? ? 48 33 C4 48 89 85 ? ? ? ?");
}
if (!addrUEP) {
addrUEP = SigScan(
"48 89 5C 24 ? 55 56 57 41 54 41 55 41 56 41 57 48 8D AC 24 "
"? ? ? ? 48 81 EC ? ? ? ? 48 8B 05 ? ? ? ? 48 33 C4 48 89 85 "
"? ? ? ? 80 B9 ? ? ? ? ? 48 8B DA 48 8B F1");
}
if (addrUEP) {
char buf[24];
std::snprintf(buf, sizeof(buf), "0x%llX", (unsigned long long)addrUEP);
Log::Info(std::string("ExitHook: UnsafeEnvironmentPopup at ") + buf);
static void* origUEP = nullptr;
HookManager::Install(reinterpret_cast<void*>(addrUEP),
reinterpret_cast<void*>(&UnsafeEnvironmentPopupStub),
&origUEP);
} else {
Log::Warn("ExitHook: UnsafeEnvironmentPopup not found (non-fatal)");
}
// RequestExitWithStatus — multiple signatures across UE versions
uintptr_t addrREWS = SigScan(
"48 89 5C 24 ? 57 48 83 EC 40 41 B9 ? ? ? ? 0F B6 F9 44 38 0D "
"? ? ? ? 0F B6 DA 72 24 89 5C 24 30 48 8D 05 ? ? ? ? 89 7C 24 28 "
"4C 8D 05 ? ? ? ? 33 D2 48 89 44 24 ? 33 C9 E8 ? ? ? ?");
if (!addrREWS) {
addrREWS = SigScan(
"48 8B C4 48 89 58 18 88 50 10 88 48 08 57 48 83 EC 30");
}
if (!addrREWS) {
addrREWS = SigScan(
"4C 8B DC 49 89 5B 08 49 89 6B 10 49 89 73 18 49 89 7B 20 "
"41 56 48 83 EC 30 80 3D ? ? ? ? ? 49 8B");
}
if (addrREWS) {
char buf[24];
std::snprintf(buf, sizeof(buf), "0x%llX", (unsigned long long)addrREWS);
Log::Info(std::string("ExitHook: RequestExitWithStatus at ") + buf);
static void* origREWS = nullptr;
HookManager::Install(reinterpret_cast<void*>(addrREWS),
reinterpret_cast<void*>(&RequestExitWithStatusStub),
&origREWS);
} else {
Log::Warn("ExitHook: RequestExitWithStatus not found (non-fatal)");
}
}
} // namespace Embeddium::ExitHook
+10
View File
@@ -0,0 +1,10 @@
#pragma once
namespace Embeddium::ExitHook {
// Scan for and suppress UnsafeEnvironmentPopup / RequestExitWithStatus
// so the game cannot force-quit itself due to env checks.
// Non-fatal: logs failure but continues.
void Install();
} // namespace Embeddium::ExitHook
+24
View File
@@ -0,0 +1,24 @@
#include "HookManager.h"
#include "../vendor/MinHook/MinHook.h"
namespace Embeddium::HookManager {
void Init() {
MH_Initialize();
}
bool Install(void* target, void* detour, void** original) {
if (MH_CreateHook(target, detour, original) != MH_OK) return false;
return MH_EnableHook(target) == MH_OK;
}
void EnableAll() {
MH_EnableHook(MH_ALL_HOOKS);
}
void Shutdown() {
MH_DisableHook(MH_ALL_HOOKS);
MH_Uninitialize();
}
} // namespace Embeddium::HookManager
+24
View File
@@ -0,0 +1,24 @@
#pragma once
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#include <Windows.h>
#include <cstdint>
namespace Embeddium::HookManager {
// Initialize MinHook — must be called before Install().
void Init();
// Install a hook: Target → Detour. Stores original pointer in *Original.
// Returns true on success.
bool Install(void* target, void* detour, void** original);
// Enable all installed hooks.
void EnableAll();
// Disable + remove all installed hooks and uninitialize MinHook.
void Shutdown();
} // namespace Embeddium::HookManager
+105
View File
@@ -0,0 +1,105 @@
#include "HttpServer.h"
#include "../HTTP/HttpRequest.h"
#include "../HTTP/HttpResponse.h"
#include "../Core/Log.h"
namespace Embeddium {
// ─────────────────────────────────────────────────────────────────────────────
// HttpServer
// ─────────────────────────────────────────────────────────────────────────────
HttpServer::HttpServer(asio::io_context& io, unsigned short port, Router& router)
: io_(io)
, acceptor_(io, tcp::endpoint(tcp::v4(), port))
, router_(router)
{
// Enable port reuse so restart doesn't get EADDRINUSE for a bit
acceptor_.set_option(asio::socket_base::reuse_address(true));
}
void HttpServer::Start() {
DoAccept();
}
void HttpServer::Stop() {
beast::error_code ec;
acceptor_.close(ec);
}
void HttpServer::DoAccept() {
acceptor_.async_accept(
asio::make_strand(io_),
[this](beast::error_code ec, tcp::socket socket) {
if (!ec) {
Log::Debug("HTTP: connection accepted");
std::make_shared<HttpSession>(std::move(socket), router_)->Start();
}
// Keep accepting if the acceptor is still open
if (acceptor_.is_open()) {
DoAccept();
}
}
);
}
// ─────────────────────────────────────────────────────────────────────────────
// HttpSession
// ─────────────────────────────────────────────────────────────────────────────
HttpSession::HttpSession(tcp::socket socket, Router& router)
: stream_(std::move(socket))
, router_(router)
, strand_(stream_.get_executor())
{
}
void HttpSession::Start() {
DoRead();
}
void HttpSession::DoRead() {
req_ = {};
stream_.expires_after(std::chrono::seconds(30));
http::async_read(stream_, buffer_, req_,
asio::bind_executor(strand_,
[self = shared_from_this()](beast::error_code ec, std::size_t) {
if (ec) return; // connection closed or error — just let session die
// Build our request wrapper
HttpRequest req(std::move(self->req_));
// Dispatch through the router
auto result = self->router_.Dispatch(req);
HttpResponse resp;
if (result.has_value()) {
resp = std::move(*result);
} else {
resp = HttpResponse::NotFound();
}
self->DoWrite(resp.Build());
}
)
);
}
void HttpSession::DoWrite(http::response<http::string_body> resp) {
// Keep-alive from request
resp.keep_alive(false);
auto sp = std::make_shared<http::response<http::string_body>>(std::move(resp));
http::async_write(stream_, *sp,
asio::bind_executor(strand_,
[self = shared_from_this(), sp](beast::error_code ec, std::size_t) {
// Graceful shutdown
beast::error_code shutdownEc;
self->stream_.socket().shutdown(tcp::socket::shutdown_send, shutdownEc);
// Connection cleanup happens automatically when shared_ptr goes out of scope
}
)
);
}
} // namespace Embeddium
+73
View File
@@ -0,0 +1,73 @@
#pragma once
#define BOOST_BEAST_USE_STD_STRING_VIEW
#define BOOST_ASIO_NO_DEPRECATED
#include <boost/asio.hpp>
#include <boost/asio/strand.hpp>
#include <boost/beast/core.hpp>
#include <boost/beast/http.hpp>
#include <memory>
#include <string>
#include "../HTTP/Router.h"
namespace Embeddium {
namespace asio = boost::asio;
namespace beast = boost::beast;
namespace http = beast::http;
using tcp = asio::ip::tcp;
// ─────────────────────────────────────────────────────────────────────────────
// HttpServer
// Async Boost.Beast HTTP server that listens on a given port and
// dispatches incoming requests through the shared Router.
//
// Threading model:
// - Acceptor loop runs on io_context (any thread calling io.run()).
// - Each accepted connection gets its own strand — all I/O and
// dispatch for that connection serialises through the strand.
// - The Router::Dispatch() call is read-only after startup — safe
// to call from any thread simultaneously.
// ─────────────────────────────────────────────────────────────────────────────
class HttpServer {
public:
// router must outlive the server.
HttpServer(asio::io_context& io, unsigned short port, Router& router);
// Start accepting connections. Non-blocking — returns immediately.
void Start();
// Stop accepting new connections.
void Stop();
private:
void DoAccept();
asio::io_context& io_;
tcp::acceptor acceptor_;
Router& router_;
};
// ─────────────────────────────────────────────────────────────────────────────
// HttpSession
// Handles a single HTTP client connection.
// Lifetime managed entirely through shared_ptr.
// ─────────────────────────────────────────────────────────────────────────────
class HttpSession : public std::enable_shared_from_this<HttpSession> {
public:
HttpSession(tcp::socket socket, Router& router);
void Start();
private:
void DoRead();
void DoWrite(http::response<http::string_body> resp);
beast::tcp_stream stream_;
beast::flat_buffer buffer_;
http::request<http::string_body> req_;
Router& router_;
asio::strand<asio::any_io_executor> strand_;
};
} // namespace Embeddium
+132
View File
@@ -0,0 +1,132 @@
#include "UriParser.h"
#include "../Utilities/StringUtils.h"
#include <algorithm>
#include <sstream>
namespace Embeddium {
// ─────────────────────────────────────────────────────────────────────────────
// Uri::Parse
// ─────────────────────────────────────────────────────────────────────────────
Uri Uri::Parse(std::string_view url) {
Uri result;
if (url.empty()) return result;
// Separate query string
size_t queryPos = url.find('?');
std::string_view base = url.substr(0, queryPos);
if (queryPos != std::string_view::npos) {
result.QueryString = std::string(url.substr(queryPos)); // includes '?'
}
// Extract protocol
size_t protoEnd = base.find("://");
if (protoEnd != std::string_view::npos) {
result.Protocol = std::string(base.substr(0, protoEnd));
base = base.substr(protoEnd + 3);
}
// Extract host[:port] and path
size_t pathStart = base.find('/');
std::string_view hostPort = (pathStart != std::string_view::npos)
? base.substr(0, pathStart)
: base;
if (pathStart != std::string_view::npos) {
result.Path = std::string(base.substr(pathStart));
}
// Split host:port
size_t colon = hostPort.find(':');
if (colon != std::string_view::npos) {
result.Host = std::string(hostPort.substr(0, colon));
result.Port = std::string(hostPort.substr(colon + 1));
} else {
result.Host = std::string(hostPort);
}
return result;
}
// ─────────────────────────────────────────────────────────────────────────────
// Uri::CreateUri
// ─────────────────────────────────────────────────────────────────────────────
std::string Uri::CreateUri(std::string_view protocol, std::string_view host,
std::string_view port, std::string_view path,
std::string_view queryString) {
std::string result;
result.reserve(128);
if (!protocol.empty()) {
result += protocol;
result += "://";
}
result += host;
if (!port.empty()) {
result += ':';
result += port;
}
result += path;
result += queryString;
return result;
}
bool Uri::HostEndsWith(std::string_view suffix) const {
return StringUtils::EndsWith(StringUtils::ToLower(Host),
StringUtils::ToLower(suffix));
}
bool Uri::HostContains(std::string_view needle) const {
return StringUtils::ContainsI(Host, needle);
}
bool Uri::PathContains(std::string_view needle) const {
return StringUtils::Contains(Path, needle);
}
// ─────────────────────────────────────────────────────────────────────────────
// QueryParams
// ─────────────────────────────────────────────────────────────────────────────
QueryParams::QueryParams(std::string_view query) {
// Strip leading '?'
if (!query.empty() && query[0] == '?') query = query.substr(1);
size_t start = 0;
while (start < query.size()) {
size_t amp = query.find('&', start);
std::string_view pair = (amp == std::string_view::npos)
? query.substr(start)
: query.substr(start, amp - start);
size_t eq = pair.find('=');
if (eq != std::string_view::npos) {
std::string key = StringUtils::UrlDecode(pair.substr(0, eq));
std::string value = StringUtils::UrlDecode(pair.substr(eq + 1));
params_.emplace(std::move(key), std::move(value));
} else if (!pair.empty()) {
params_.emplace(StringUtils::UrlDecode(pair), "");
}
start = (amp == std::string_view::npos) ? query.size() : amp + 1;
}
}
std::string QueryParams::Get(std::string_view name, std::string_view def) const {
auto it = params_.find(std::string(name));
if (it == params_.end()) return std::string(def);
return it->second;
}
bool QueryParams::Has(std::string_view name) const {
return params_.count(std::string(name)) > 0;
}
std::vector<std::string> QueryParams::GetAll(std::string_view name) const {
std::vector<std::string> result;
auto range = params_.equal_range(std::string(name));
for (auto it = range.first; it != range.second; ++it) {
result.push_back(it->second);
}
return result;
}
} // namespace Embeddium
+68
View File
@@ -0,0 +1,68 @@
#pragma once
#include <string>
#include <string_view>
#include <unordered_map>
namespace Embeddium {
// ─────────────────────────────────────────────────────────────────────────────
// Uri
// Parsed representation of a URL.
// Used by the curl hook to inspect and rewrite URLs, and by the HTTP
// server to extract path + query components from incoming requests.
// ─────────────────────────────────────────────────────────────────────────────
struct Uri {
std::string Protocol; // e.g. "http"
std::string Host; // e.g. "account-public-service-prod.ol.epicgames.com"
std::string Port; // e.g. "443" (may be empty)
std::string Path; // e.g. "/account/api/public/account"
std::string QueryString; // e.g. "?accountId=abc" (includes leading '?')
// Parse a full URL into components.
static Uri Parse(std::string_view url);
// Reconstruct a URL from components.
static std::string CreateUri(std::string_view protocol,
std::string_view host,
std::string_view port,
std::string_view path,
std::string_view queryString);
// Returns true if the host ends with the given suffix (case-insensitive).
bool HostEndsWith(std::string_view suffix) const;
// Returns true if the host contains the given substring (case-insensitive).
bool HostContains(std::string_view needle) const;
// Returns true if the path contains the given substring (case-sensitive).
bool PathContains(std::string_view needle) const;
};
// ─────────────────────────────────────────────────────────────────────────────
// QueryParams
// Simple parser for URL query strings.
// e.g. "?accountId=abc&foo=bar" → { "accountId": "abc", "foo": "bar" }
// ─────────────────────────────────────────────────────────────────────────────
class QueryParams {
public:
// Parse a query string (with or without leading '?').
explicit QueryParams(std::string_view query);
// Get a single value by name. Returns "" if not found.
std::string Get(std::string_view name, std::string_view def = "") const;
// Returns true if the named key exists.
bool Has(std::string_view name) const;
// Returns all values for a given name (e.g. ?accountId=a&accountId=b).
std::vector<std::string> GetAll(std::string_view name) const;
const std::unordered_multimap<std::string, std::string>& All() const {
return params_;
}
private:
std::unordered_multimap<std::string, std::string> params_;
};
} // namespace Embeddium
+197
View File
@@ -0,0 +1,197 @@
#include "WebSocketServer.h"
#include "../Core/Log.h"
#include <boost/beast/http.hpp>
namespace Embeddium {
namespace http = beast::http;
// ─────────────────────────────────────────────────────────────────────────────
// WebSocketSession
// ─────────────────────────────────────────────────────────────────────────────
WebSocketSession::WebSocketSession(tcp::socket socket, asio::io_context& io)
: ws_(std::move(socket))
, strand_(asio::make_strand(io))
{
}
void WebSocketSession::Start(std::function<void(Ptr)> onConnected) {
// Move onto the strand for all subsequent operations
asio::dispatch(strand_,
[self = shared_from_this(), cb = std::move(onConnected)]() mutable {
self->DoHandshake(std::move(cb));
});
}
void WebSocketSession::DoHandshake(std::function<void(Ptr)> onConnected) {
// Allow any subprotocol — we'll read what was negotiated after the handshake
ws_.set_option(websocket::stream_base::decorator(
[this](websocket::response_type& res) {
// Echo back the first requested subprotocol so the client is happy
auto it = res.find(http::field::sec_websocket_protocol);
(void)it; // May not be present
}));
// We need to read the HTTP upgrade request first to capture the
// Sec-WebSocket-Protocol header, then finish the handshake manually.
// Beast's async_accept with a handler gives us the request.
auto req = std::make_shared<http::request<http::string_body>>();
http::async_read(ws_.next_layer(), readBuf_, *req,
asio::bind_executor(strand_,
[self = shared_from_this(), req, cb = std::move(onConnected)]
(beast::error_code ec, std::size_t) mutable {
if (ec) return;
// Capture the negotiated subprotocol
auto proto = req->find(http::field::sec_websocket_protocol);
if (proto != req->end()) {
// Take the first value (before any comma)
std::string_view sv = proto->value();
auto comma = sv.find(',');
self->protocol_ = std::string(sv.substr(0, comma));
// trim whitespace
while (!self->protocol_.empty() && self->protocol_.front() == ' ')
self->protocol_.erase(self->protocol_.begin());
while (!self->protocol_.empty() && self->protocol_.back() == ' ')
self->protocol_.pop_back();
}
// Complete the WebSocket handshake, echoing back the subprotocol
self->ws_.async_accept(*req,
asio::bind_executor(self->strand_,
[self, cb = std::move(cb)](beast::error_code ec2) mutable {
if (ec2) return;
// If there was a subprotocol, confirm it in the
// response decorator (already negotiated above).
// Notify the factory.
cb(self);
// Begin reading messages
self->DoRead();
}));
}));
}
void WebSocketSession::DoRead() {
ws_.async_read(readBuf_,
asio::bind_executor(strand_,
[self = shared_from_this()](beast::error_code ec, std::size_t) {
if (ec) {
// Connection closed or error
if (self->onClose_) self->onClose_();
return;
}
// Deliver the message
if (self->onMessage_) {
auto data = beast::buffers_to_string(self->readBuf_.data());
self->readBuf_.consume(self->readBuf_.size());
self->onMessage_(data);
} else {
self->readBuf_.consume(self->readBuf_.size());
}
// Continue reading
self->DoRead();
}));
}
void WebSocketSession::DoWrite() {
if (writeQueue_.empty()) {
writing_ = false;
return;
}
ws_.async_write(asio::buffer(writeQueue_.front()),
asio::bind_executor(strand_,
[self = shared_from_this()](beast::error_code ec, std::size_t) {
if (ec) {
if (self->onClose_) self->onClose_();
return;
}
self->writeQueue_.erase(self->writeQueue_.begin());
self->DoWrite();
}));
}
void WebSocketSession::SendText(std::string msg) {
asio::dispatch(strand_,
[self = shared_from_this(), m = std::move(msg)]() mutable {
if (!self->ws_.is_open()) return;
self->writeQueue_.push_back(std::move(m));
if (!self->writing_) {
self->writing_ = true;
self->ws_.text(true);
self->DoWrite();
}
});
}
void WebSocketSession::SendBinary(std::string msg) {
asio::dispatch(strand_,
[self = shared_from_this(), m = std::move(msg)]() mutable {
if (!self->ws_.is_open()) return;
self->writeQueue_.push_back(std::move(m));
if (!self->writing_) {
self->writing_ = true;
self->ws_.binary(true);
self->DoWrite();
}
});
}
void WebSocketSession::Close() {
asio::dispatch(strand_,
[self = shared_from_this()]() {
if (!self->ws_.is_open()) return;
self->ws_.async_close(websocket::close_code::normal,
asio::bind_executor(self->strand_,
[self](beast::error_code) {
if (self->onClose_) self->onClose_();
}));
});
}
// ─────────────────────────────────────────────────────────────────────────────
// WebSocketServer
// ─────────────────────────────────────────────────────────────────────────────
WebSocketServer::WebSocketServer(asio::io_context& io, unsigned short port,
SessionFactory factory)
: io_(io)
, acceptor_(io, tcp::endpoint(tcp::v4(), port))
, factory_(std::move(factory))
{
acceptor_.set_option(asio::socket_base::reuse_address(true));
}
void WebSocketServer::Start() {
DoAccept();
}
void WebSocketServer::Stop() {
beast::error_code ec;
acceptor_.close(ec);
}
void WebSocketServer::DoAccept() {
acceptor_.async_accept(
asio::make_strand(io_),
[this](beast::error_code ec, tcp::socket socket) {
if (!ec) {
auto session = std::make_shared<WebSocketSession>(
std::move(socket), io_);
session->SetId(nextId_++);
Log::Debug("WS: connection accepted, id=" + std::to_string(session->Id()));
session->Start(factory_);
}
// Keep accepting even on error (unless acceptor is closed)
if (acceptor_.is_open()) DoAccept();
});
}
} // namespace Embeddium
+117
View File
@@ -0,0 +1,117 @@
#pragma once
#define BOOST_BEAST_USE_STD_STRING_VIEW
#define BOOST_ASIO_NO_DEPRECATED
#include <boost/asio.hpp>
#include <boost/asio/strand.hpp>
#include <boost/beast/core.hpp>
#include <boost/beast/websocket.hpp>
#include <memory>
#include <functional>
#include <string>
#include <string_view>
namespace Embeddium {
namespace asio = boost::asio;
namespace beast = boost::beast;
namespace websocket = beast::websocket;
using tcp = asio::ip::tcp;
// ─────────────────────────────────────────────────────────────────────────────
// WebSocketSession
// A single accepted WebSocket connection.
// The concrete session type (XMPP or Matchmaker) is chosen by the
// factory callback based on the Sec-WebSocket-Protocol header.
//
// Lifetime: managed by shared_ptr.
// Threading: each session is strand-serialised — no mutexes.
// ─────────────────────────────────────────────────────────────────────────────
class WebSocketSession : public std::enable_shared_from_this<WebSocketSession> {
public:
using Ptr = std::shared_ptr<WebSocketSession>;
WebSocketSession(tcp::socket socket, asio::io_context& io);
// Begin the WebSocket handshake, then call the provided factory to set up
// message handling. Called once by WebSocketServer.
void Start(std::function<void(Ptr)> onConnected);
// Send a text message asynchronously (strand-safe, queued).
void SendText(std::string msg);
// Send a binary message asynchronously.
void SendBinary(std::string msg);
// Close the connection gracefully.
void Close();
// Returns the negotiated subprotocol (e.g. "xmpp")
std::string_view Protocol() const { return protocol_; }
// Returns true if this session is still open
bool IsOpen() const { return ws_.is_open(); }
// Unique session ID (assigned by WebSocketServer at accept time)
uint64_t Id() const { return id_; }
void SetId(uint64_t id) { id_ = id; }
// Set the message handler — called on every incoming text frame.
void SetMessageHandler(std::function<void(std::string_view)> handler) {
onMessage_ = std::move(handler);
}
// Set the close handler — called when the connection is closed.
void SetCloseHandler(std::function<void()> handler) {
onClose_ = std::move(handler);
}
private:
void DoHandshake(std::function<void(Ptr)> onConnected);
void DoRead();
void DoWrite();
websocket::stream<beast::tcp_stream> ws_;
asio::strand<asio::io_context::executor_type> strand_;
beast::flat_buffer readBuf_;
std::vector<std::string> writeQueue_;
bool writing_ = false;
std::string protocol_;
uint64_t id_ = 0;
std::function<void(std::string_view)> onMessage_;
std::function<void()> onClose_;
};
// ─────────────────────────────────────────────────────────────────────────────
// WebSocketServer
// Accepts TCP connections on a port and upgrades them to WebSocket.
// Routes each accepted connection to either XmppClient or MatchmakerSession
// based on the Sec-WebSocket-Protocol header.
//
// The session factory callback is set by Initializer after both XMPP
// and Matchmaker implementations are constructed.
// ─────────────────────────────────────────────────────────────────────────────
class WebSocketServer {
public:
// factory is called with each new WebSocketSession immediately after
// the WebSocket handshake completes.
using SessionFactory = std::function<void(WebSocketSession::Ptr)>;
WebSocketServer(asio::io_context& io, unsigned short port,
SessionFactory factory);
void Start();
void Stop();
private:
void DoAccept();
asio::io_context& io_;
tcp::acceptor acceptor_;
SessionFactory factory_;
uint64_t nextId_ = 1;
};
} // namespace Embeddium
+459
View File
@@ -0,0 +1,459 @@
#include "McpRoutes.h"
#include "../HTTP/HttpRequest.h"
#include "../HTTP/HttpResponse.h"
#include "../Backend/AccountState.h"
#include "../Backend/ProfileStore.h"
#include "../Backend/VersionService.h"
#include "../Utilities/JsonUtils.h"
#include "../Utilities/UuidGenerator.h"
#include "../Core/Log.h"
#include <chrono>
#include <sstream>
#include <iomanip>
#include <string>
#include <algorithm>
#include <unordered_set>
namespace Embeddium::McpRoutes {
// ─────────────────────────────────────────────────────────────────────────────
// Internal helpers
// ─────────────────────────────────────────────────────────────────────────────
static std::string NowIso() {
auto now = std::chrono::system_clock::now();
auto t = std::chrono::system_clock::to_time_t(now);
std::tm tm{};
gmtime_s(&tm, &t);
std::ostringstream ss;
ss << std::put_time(&tm, "%Y-%m-%dT%H:%M:%S.000Z");
return ss.str();
}
// Build the outer MCP response envelope around an inner profileChanges array.
static Json MakeEnvelope(const std::string& profileId,
int queryRevision,
Json profileChanges) {
Json* prof = ProfileStore::GetProfile(profileId);
int rvn = prof ? prof->value("rvn", 0) : 0;
int crvn = prof ? prof->value("commandRevision", 0) : 0;
return {
{"profileRevision", rvn},
{"profileId", profileId},
{"profileChangesBaseRevision", queryRevision >= 0 ? queryRevision : rvn},
{"profileChanges", std::move(profileChanges)},
{"profileCommandRevision", crvn},
{"serverTime", NowIso()},
{"responseVersion", 1}
};
}
// Return a fullProfileUpdate change array for the given profileId.
static Json FullUpdate(const std::string& profileId) {
Json* prof = ProfileStore::GetProfile(profileId);
if (!prof) return Json::array();
return Json::array({{{"changeType","fullProfileUpdate"},{"profile",*prof}}});
}
// Find the first item key in a profile whose templateId starts with a prefix (case-insensitive).
static std::string FindItemByPrefix(const Json& profile, std::string_view prefix) {
if (!profile.contains("items") || !profile["items"].is_object()) return {};
std::string pfx(prefix);
std::transform(pfx.begin(), pfx.end(), pfx.begin(), ::tolower);
for (const auto& [key, item] : profile["items"].items()) {
std::string tid = item.value("templateId", "");
std::transform(tid.begin(), tid.end(), tid.begin(), ::tolower);
if (tid.rfind(pfx, 0) == 0) return key;
}
return {};
}
// Map Fortnite slot names → locker slot key used in the profile JSON
static std::string SlotKey(const std::string& slotName) {
// The game uses these exact strings; the profile JSON uses the same.
return slotName;
}
// ─────────────────────────────────────────────────────────────────────────────
// Core command dispatcher
// ─────────────────────────────────────────────────────────────────────────────
static HttpResponse HandleMcp(HttpRequest& req) {
std::string accountId = req.Param("accountId");
std::string command = req.Param("command");
std::string profileId = req.Query("profileId");
if (profileId.empty()) profileId = "athena";
Log::Debug("MCP: " + command + " [" + profileId + "] account=" + accountId);
int rvn = -1;
try { rvn = std::stoi(req.Query("rvn")); } catch (...) {}
auto body = req.BodyJson();
auto ver = VersionService::Parse(req.UserAgent());
// Re-init ProfileStore if the season in the User-Agent differs from what
// was loaded at startup (season is embedded in the UA build number).
ProfileStore::Init(ver.season, ver.build);
// Update accountId in profile items if the profile references a placeholder.
{
Json* prof = ProfileStore::GetProfile(profileId);
if (prof && prof->contains("accountId")) {
(*prof)["accountId"] = accountId;
}
}
// ── No-op commands — return fullProfileUpdate with no changes ─────────────
static const std::unordered_set<std::string> kNoOp = {
"QueryProfile",
"ClientQuestLogin",
"RecordUserEvent",
"RemoveGiftBox",
"MarkNewQuestNotificationSent",
"ClaimLoginReward",
"SetBattlePassPurchased",
"FortRerollDailyQuests",
"IncrementNamedCounterStat",
"SetMtxPlatform",
"UnlockRewardNode",
"GetDailyMissionRewards",
"VerifyRealMoneyPurchase",
"PurchaseCatalogEntry",
"GiftCatalogEntry",
"CopySkin",
"RefundMtxPurchase",
"SetPinnedQuests",
"DestroyWorldItem",
"SetActiveBannerData",
"SelectRandomViewedCollection",
};
if (kNoOp.count(command)) {
// Just make sure the revision is current; no attribute mutation.
return HttpResponse().Json(MakeEnvelope(profileId, rvn, FullUpdate(profileId)));
}
// ── SetCosmeticLockerSlot / EquipBattleRoyaleCustomization ───────────────
if (command == "SetCosmeticLockerSlot" || command == "EquipBattleRoyaleCustomization") {
Json* prof = ProfileStore::GetProfile("athena");
if (!prof) return HttpResponse().Json(MakeEnvelope("athena", rvn, FullUpdate("athena")));
std::string slotName = body.value("slotName", body.value("category", ""));
std::string itemToSlot = body.value("itemToSlot", body.value("itemVariant", ""));
int indexWithinSlot= body.value("indexWithinSlot", body.value("variantChannelTag", 0));
std::string lockerItemKey = body.value("lockerItem", "");
// Resolve lockerItem key if not provided
if (lockerItemKey.empty())
lockerItemKey = FindItemByPrefix(*prof, "CosmeticLocker:");
// If still not found by prefix, accept the key as-is from the body
if (lockerItemKey.empty() || !(*prof)["items"].contains(lockerItemKey)) {
// Fallback: first key that contains "CosmeticLocker" case-insensitively
for (const auto& [key, item] : (*prof)["items"].items()) {
std::string tid = item.value("templateId","");
std::transform(tid.begin(), tid.end(), tid.begin(), ::tolower);
if (tid.find("cosmeticlocker") != std::string::npos) {
lockerItemKey = key;
break;
}
}
}
if (!lockerItemKey.empty() && (*prof)["items"].contains(lockerItemKey)) {
auto& locker = (*prof)["items"][lockerItemKey];
auto& attrs = locker["attributes"];
auto& slots = attrs["locker_slots_data"]["slots"];
auto& slotObj = slots[SlotKey(slotName)];
if (!slotObj.contains("items") || !slotObj["items"].is_array())
slotObj["items"] = Json::array({""});
// Expand the items array if needed
while ((int)slotObj["items"].size() <= indexWithinSlot)
slotObj["items"].push_back("");
slotObj["items"][indexWithinSlot] = itemToSlot;
// Handle activeVariants if provided
if (body.contains("variantUpdates") && body["variantUpdates"].is_array()) {
if (!slotObj.contains("activeVariants") || !slotObj["activeVariants"].is_array())
slotObj["activeVariants"] = Json::array();
while ((int)slotObj["activeVariants"].size() <= indexWithinSlot)
slotObj["activeVariants"].push_back(nullptr);
slotObj["activeVariants"][indexWithinSlot] = body["variantUpdates"];
}
ProfileStore::BumpRevision("athena");
Json changes = Json::array({{
{"changeType", "itemAttrChanged"},
{"itemId", lockerItemKey},
{"attributeName", "locker_slots_data"},
{"attributeValue",attrs["locker_slots_data"]}
}});
return HttpResponse().Json(MakeEnvelope("athena", rvn, std::move(changes)));
}
// Fallback: fullProfileUpdate
ProfileStore::BumpRevision("athena");
return HttpResponse().Json(MakeEnvelope("athena", rvn, FullUpdate("athena")));
}
// ── SetItemFavoriteStatusBatch / SetItemFavoriteStatus ───────────────────
if (command == "SetItemFavoriteStatusBatch" || command == "SetItemFavoriteStatus") {
Json* prof = ProfileStore::GetProfile("athena");
if (!prof) return HttpResponse().Json(MakeEnvelope("athena", rvn, FullUpdate("athena")));
Json changes = Json::array();
if (command == "SetItemFavoriteStatusBatch") {
// itemIds: array, itemFavStatus: array of bool
auto& itemIds = body["itemIds"];
auto& itemStatus = body["itemFavStatus"];
if (itemIds.is_array()) {
for (size_t i = 0; i < itemIds.size(); ++i) {
std::string itemId = itemIds[i].get<std::string>();
bool favored = itemStatus.is_array() && i < itemStatus.size()
? itemStatus[i].get<bool>() : false;
if ((*prof)["items"].contains(itemId)) {
(*prof)["items"][itemId]["attributes"]["favorite"] = favored;
changes.push_back({
{"changeType", "itemAttrChanged"},
{"itemId", itemId},
{"attributeName", "favorite"},
{"attributeValue",favored}
});
}
}
}
} else {
// SetItemFavoriteStatus: single itemId + itemFavStatus
std::string itemId = body.value("itemId", "");
bool favored = body.value("itemFavStatus", false);
if (!itemId.empty() && (*prof)["items"].contains(itemId)) {
(*prof)["items"][itemId]["attributes"]["favorite"] = favored;
changes.push_back({
{"changeType", "itemAttrChanged"},
{"itemId", itemId},
{"attributeName", "favorite"},
{"attributeValue",favored}
});
}
}
if (!changes.empty()) {
ProfileStore::BumpRevision("athena");
}
return HttpResponse().Json(MakeEnvelope("athena", rvn, std::move(changes)));
}
// ── SetBattleRoyaleBanner ─────────────────────────────────────────────────
if (command == "SetBattleRoyaleBanner") {
// Updates common_core banner, but game sends to athena profile.
// Game sends banner updates to athena profile; update stats there.
Json* prof = ProfileStore::GetProfile("athena");
if (!prof) return HttpResponse().Json(MakeEnvelope("athena", rvn, FullUpdate("athena")));
std::string icon = body.value("homebaseBannerIconId", "");
std::string color = body.value("homebaseBannerColorId", "");
if (!icon.empty()) (*prof)["stats"]["attributes"]["banner_icon"] = icon;
if (!color.empty()) (*prof)["stats"]["attributes"]["banner_color"] = color;
// Also update common_core if it exists
Json* cc = ProfileStore::GetProfile("common_core");
if (cc) {
if (!icon.empty()) (*cc)["stats"]["attributes"]["banner_icon"] = icon;
if (!color.empty()) (*cc)["stats"]["attributes"]["banner_color"] = color;
}
ProfileStore::BumpRevision("athena");
Json changes = Json::array({{
{"changeType","statModified"},
{"name","banner_icon"},{"value",icon}
},{
{"changeType","statModified"},
{"name","banner_color"},{"value",color}
}});
return HttpResponse().Json(MakeEnvelope("athena", rvn, std::move(changes)));
}
// ── SetPartyAssistQuest ───────────────────────────────────────────────────
if (command == "SetPartyAssistQuest") {
Json* prof = ProfileStore::GetProfile("athena");
if (!prof) return HttpResponse().Json(MakeEnvelope("athena", rvn, FullUpdate("athena")));
std::string quest = body.value("questToPinPath", "");
(*prof)["stats"]["attributes"]["party_assist_quest"] = quest;
ProfileStore::BumpRevision("athena");
Json changes = Json::array({{
{"changeType","statModified"},
{"name","party_assist_quest"},
{"value",quest}
}});
return HttpResponse().Json(MakeEnvelope("athena", rvn, std::move(changes)));
}
// ── SetAffiliate (SAC code) ───────────────────────────────────────────────
if (command == "SetAffiliate") {
Json* prof = ProfileStore::GetProfile("common_core");
if (!prof) return HttpResponse().Json(MakeEnvelope("common_core", rvn, FullUpdate("common_core")));
std::string affiliate = body.value("affiliateId", "");
(*prof)["stats"]["attributes"]["mtx_affiliate"] = affiliate;
(*prof)["stats"]["attributes"]["mtx_affiliate_set_time"] = NowIso();
(*prof)["stats"]["attributes"]["mtx_affiliate_id"] = "";
ProfileStore::BumpRevision("common_core");
Json changes = Json::array({{
{"changeType","statModified"},
{"name","mtx_affiliate"},
{"value",affiliate}
}});
return HttpResponse().Json(MakeEnvelope("common_core", rvn, std::move(changes)));
}
// ── SetReceiveGiftsEnabled ────────────────────────────────────────────────
if (command == "SetReceiveGiftsEnabled") {
Json* prof = ProfileStore::GetProfile("common_core");
if (!prof) return HttpResponse().Json(MakeEnvelope("common_core", rvn, FullUpdate("common_core")));
bool enabled = body.value("bReceiveGifts", true);
(*prof)["stats"]["attributes"]["receive_gifts_enabled"] = enabled;
ProfileStore::BumpRevision("common_core");
Json changes = Json::array({{
{"changeType","statModified"},
{"name","receive_gifts_enabled"},
{"value",enabled}
}});
return HttpResponse().Json(MakeEnvelope("common_core", rvn, std::move(changes)));
}
// ── SetItemArchivedStatusBatch ────────────────────────────────────────────
if (command == "SetItemArchivedStatusBatch") {
Json* prof = ProfileStore::GetProfile("athena");
if (!prof) return HttpResponse().Json(MakeEnvelope("athena", rvn, FullUpdate("athena")));
Json changes = Json::array();
auto& itemIds = body["itemIds"];
auto& archivedStatus = body["itemArchivedStatus"];
if (itemIds.is_array()) {
for (size_t i = 0; i < itemIds.size(); ++i) {
std::string itemId = itemIds[i].get<std::string>();
bool archived = archivedStatus.is_array() && i < archivedStatus.size()
? archivedStatus[i].get<bool>() : false;
if ((*prof)["items"].contains(itemId)) {
(*prof)["items"][itemId]["attributes"]["archived"] = archived;
changes.push_back({
{"changeType", "itemAttrChanged"},
{"itemId", itemId},
{"attributeName", "archived"},
{"attributeValue",archived}
});
}
}
}
if (!changes.empty()) ProfileStore::BumpRevision("athena");
return HttpResponse().Json(MakeEnvelope("athena", rvn, std::move(changes)));
}
// ── SetCosmeticLockerName ─────────────────────────────────────────────────
if (command == "SetCosmeticLockerName") {
Json* prof = ProfileStore::GetProfile("athena");
if (!prof) return HttpResponse().Json(MakeEnvelope("athena", rvn, FullUpdate("athena")));
std::string lockerItemKey = body.value("lockerItem", "");
std::string lockerName = body.value("name", "");
if (!lockerItemKey.empty() && (*prof)["items"].contains(lockerItemKey)) {
(*prof)["items"][lockerItemKey]["attributes"]["locker_name"] = lockerName;
ProfileStore::BumpRevision("athena");
Json changes = Json::array({{
{"changeType", "itemAttrChanged"},
{"itemId", lockerItemKey},
{"attributeName", "locker_name"},
{"attributeValue",lockerName}
}});
return HttpResponse().Json(MakeEnvelope("athena", rvn, std::move(changes)));
}
return HttpResponse().Json(MakeEnvelope("athena", rvn, FullUpdate("athena")));
}
// ── SetVariantMarkedSeen ──────────────────────────────────────────────────
if (command == "SetVariantMarkedSeen") {
Json* prof = ProfileStore::GetProfile("athena");
if (!prof) return HttpResponse().Json(MakeEnvelope("athena", rvn, FullUpdate("athena")));
Json changes = Json::array();
if (body.contains("request") && body["request"].is_object()) {
for (const auto& [itemId, seenVariants] : body["request"].items()) {
if ((*prof)["items"].contains(itemId)) {
auto& attrs = (*prof)["items"][itemId]["attributes"];
if (!attrs.contains("variants") || !attrs["variants"].is_array()) continue;
for (auto& variant : attrs["variants"]) {
std::string ch = variant.value("channel","");
if (seenVariants.contains(ch) && seenVariants[ch].is_array()) {
for (const auto& seen : seenVariants[ch]) {
std::string tag = seen.get<std::string>();
bool found = false;
if (variant.contains("owned") && variant["owned"].is_array()) {
for (const auto& o : variant["owned"])
if (o.get<std::string>() == tag) { found = true; break; }
}
if (!found) {
if (!variant.contains("owned")) variant["owned"] = Json::array();
variant["owned"].push_back(tag);
}
}
}
}
changes.push_back({
{"changeType", "itemAttrChanged"},
{"itemId", itemId},
{"attributeName", "variants"},
{"attributeValue",attrs["variants"]}
});
}
}
}
if (!changes.empty()) ProfileStore::BumpRevision("athena");
return HttpResponse().Json(MakeEnvelope("athena", rvn, std::move(changes)));
}
// ── SetActiveHeroLoadout (STW campaign) ───────────────────────────────────
if (command == "SetActiveHeroLoadout") {
// No-op for BR; campaign profile update
return HttpResponse().Json(MakeEnvelope(profileId, rvn, FullUpdate(profileId)));
}
// ── Unknown / catch-all ───────────────────────────────────────────────────
return HttpResponse().Json(MakeEnvelope(profileId, rvn, FullUpdate(profileId)));
}
// ─────────────────────────────────────────────────────────────────────────────
// RegisterAll
// ─────────────────────────────────────────────────────────────────────────────
void RegisterAll(Router& router) {
// All three MCP endpoint prefixes map to the same dispatcher
router.Post("/fortnite/api/game/v2/profile/:accountId/client/:command",
HandleMcp);
router.Post("/fortnite/api/game/v2/profile/:accountId/dedicated_server/:command",
HandleMcp);
router.Post("/fortnite/api/game/v2/profile/:accountId/public/:command",
HandleMcp);
// Season/build init ping — game sends this before first MCP call
router.Post("/fortnite/api/game/v2/profile/:accountId/client/QueryProfile",
HandleMcp);
}
} // namespace Embeddium::McpRoutes
+10
View File
@@ -0,0 +1,10 @@
#pragma once
#include "../HTTP/Router.h"
namespace Embeddium::McpRoutes {
// Registers all MCP (profile) routes into the given router.
// Called from Routes::RegisterAll after all stateless routes.
void RegisterAll(Router& router);
} // namespace Embeddium::McpRoutes
+670
View File
@@ -0,0 +1,670 @@
#include "Routes.h"
#include "McpRoutes.h"
#include "../HTTP/HttpRequest.h"
#include "../HTTP/HttpResponse.h"
#include "../Assets/AssetManager.h"
#include "../Backend/AccountState.h"
#include "../Backend/FriendStore.h"
#include "../Backend/PrivacyStore.h"
#include "../Backend/ProfileStore.h"
#include "../Backend/ItemShopService.h"
#include "../Backend/TimelineService.h"
#include "../Backend/ContentService.h"
#include "../Backend/CloudStorageService.h"
#include "../Backend/TheaterService.h"
#include "../Backend/VersionService.h"
#include "../Core/Config.h"
#include "../Utilities/JsonUtils.h"
#include "../Utilities/UuidGenerator.h"
#include "../Utilities/StringUtils.h"
#include <chrono>
#include <sstream>
#include <iomanip>
#include <string>
namespace Embeddium::Routes {
// ─────────────────────────────────────────────────────────────────────────────
// Helpers
// ─────────────────────────────────────────────────────────────────────────────
static std::string NowIso() {
auto now = std::chrono::system_clock::now();
auto t = std::chrono::system_clock::to_time_t(now);
std::tm tm{};
gmtime_s(&tm, &t);
std::ostringstream ss;
ss << std::put_time(&tm, "%Y-%m-%dT%H:%M:%S.000Z");
return ss.str();
}
static VersionInfo GetVer(const HttpRequest& req) {
return VersionService::Parse(req.Header("User-Agent"));
}
// ─────────────────────────────────────────────────────────────────────────────
// RegisterAll — registers every HTTP route
// ─────────────────────────────────────────────────────────────────────────────
void RegisterAll(Router& router) {
router.Get("/fortnite/api/version", [](HttpRequest&) {
return HttpResponse().Json({
{"app","fortnite"},{"serverDate",NowIso()},
{"overridePropertiesVersion","unknown"},{"cln","17951730"},
{"build","444"},{"moduleName","Fortnite-Core"},
{"buildDate","2021-10-27T21:00:51.697Z"},
{"version","18.30"},{"branch","Release-18.30"},
{"modules",{
{"Epic-LightSwitch-AccessControlCore",{
{"cln","17237679"},{"build","b2130"},
{"buildDate","2021-08-19T18:56:08.144Z"},
{"version","1.0.0"},{"branch","trunk"}}},
{"epic-xmpp-api-v1-base",{
{"cln","5131a23c1470acbd9c94fae695ef7d899c1a41d6"},
{"build","b3595"},{"buildDate","2019-07-30T09:11:06.587Z"},
{"version","0.0.1"},{"branch","master"}}},
{"epic-common-core",{
{"cln","17909521"},{"build","3217"},
{"buildDate","2021-10-25T18:41:12.486Z"},
{"version","3.0"},{"branch","TRUNK"}}}
}}
});
});
router.Get("/fortnite/api/v2/versioncheck/*", [](HttpRequest&) {
return HttpResponse().Json({{"type","NO_UPDATE"}});
});
router.Get("/fortnite/api/v2/versioncheck*", [](HttpRequest&) {
return HttpResponse().Json({{"type","NO_UPDATE"}});
});
router.Get("/fortnite/api/versioncheck*", [](HttpRequest&) {
return HttpResponse().Json({{"type","NO_UPDATE"}});
});
router.Get("/lightswitch/api/service/Fortnite/status", [](HttpRequest&) {
return HttpResponse().Json({
{"serviceInstanceId","fortnite"},{"status","UP"},
{"message","Fortnite is online"},{"maintenanceUri",nullptr},
{"overrideCatalogIds",Json::array({"a7f138b2e51945ffbfdacc1af0541053"})},
{"allowedActions",Json::array()},{"banned",false},
{"launcherInfoDTO",{
{"appName","Fortnite"},
{"catalogItemId","4fe75bbc5a674f4f9b356b5c90567da5"},
{"namespace","fn"}}}
});
});
router.Get("/lightswitch/api/service/bulk/status", [](HttpRequest&) {
return HttpResponse().Json(Json::array({{
{"serviceInstanceId","fortnite"},{"status","UP"},
{"message","fortnite is up."},{"maintenanceUri",nullptr},
{"overrideCatalogIds",Json::array({"a7f138b2e51945ffbfdacc1af0541053"})},
{"allowedActions",Json::array({"PLAY","DOWNLOAD"})},
{"banned",false},
{"launcherInfoDTO",{
{"appName","Fortnite"},
{"catalogItemId","4fe75bbc5a674f4f9b356b5c90567da5"},
{"namespace","fn"}}}
}}));
});
router.Get("/account/api/public/account", [](HttpRequest& req) {
Json arr = Json::array();
std::string id = req.Query("accountId");
if (!id.empty()) {
auto at = id.find('@');
if (at != std::string::npos) id = id.substr(0, at);
arr.push_back({{"id",id},{"displayName",id},{"externalAuths",Json::object()}});
}
return HttpResponse().Json(arr);
});
router.Get("/account/api/public/account/:accountId", [](HttpRequest& req) {
std::string accountId = req.Param("accountId");
auto at = accountId.find('@');
if (at != std::string::npos) accountId = accountId.substr(0, at);
AccountState::SetFromLogin(accountId);
std::string name = AccountState::Get();
return HttpResponse().Json({
{"id",req.Param("accountId")},{"displayName",name},{"name","Embeddium"},
{"email",name+"@embeddium.com"},{"failedLoginAttempts",0},
{"lastLogin",NowIso()},{"numberOfDisplayNameChanges",0},
{"ageGroup","UNKNOWN"},{"headless",false},{"country","US"},
{"lastName","Embeddium"},{"preferredLanguage","en"},
{"canUpdateDisplayName",false},{"tfaEnabled",false},
{"emailVerified",true},{"minorVerified",false},{"minorExpected",false},
{"minorStatus","NOT_MINOR"},{"cabinedMode",false},{"hasHashedEmail",false}
});
});
router.Post("/account/api/oauth/token", [](HttpRequest& req) {
std::string username;
auto body = req.BodyJson();
if (body.is_object()) username = body.value("username","");
if (username.empty()) username = req.Query("username");
if (!username.empty()) AccountState::SetFromLogin(username);
std::string name = AccountState::Get();
return HttpResponse().Json({
{"access_token","embeddiumtokenlol"},{"expires_in",28800},
{"expires_at","9999-12-02T01:12:01.100Z"},{"token_type","bearer"},
{"refresh_token","embeddiumtokenlol"},{"refresh_expires",86400},
{"refresh_expires_at","9999-12-02T01:12:01.100Z"},
{"account_id",name},{"client_id","embeddiumclientidlol"},
{"internal_client",true},{"client_service","fortnite"},
{"displayName",name},{"app","fortnite"},{"in_app_id",name},
{"device_id","embeddiumdeviceidlol"}
});
});
router.Post("/auth/v1/oauth/token", [](HttpRequest&) {
std::string name = AccountState::Get();
return HttpResponse().Json({
{"access_token","embeddiumtokenlol"},{"token_type","bearer"},
{"expires_in",28800},{"expires_at","9999-12-31T23:59:59.999Z"},
{"nonce","embeddiumserver"},
{"features",Json::array({"AntiCheat","CommerceService","Connect",
"ContentService","Ecom","EpicConnect","Inventories","LockerService",
"MagpieService","Matchmaking Service","PCBService","QuestService","Stats"})},
{"deployment_id","embeddiumdeploymentidlol"},
{"organization_id","embeddiumorganizationidlol"},
{"organization_user_id","embeddiumorganisationuseridlol"},
{"product_id","prod-fn"},{"product_user_id","embeddiumproductuseridlol"},
{"product_user_id_created",false},
{"id_token","embeddiumidtokenlol"},{"sandbox_id","fn"}
});
});
router.Post("/epic/oauth/v2/token", [](HttpRequest&) {
std::string name = AccountState::Get();
return HttpResponse().Json({
{"scope","basic_profile friends_list openid presence"},
{"token_type","bearer"},{"access_token","embeddiumtokenlol"},
{"expires_in",28800},{"expires_at","9999-12-31T23:59:59.999Z"},
{"refresh_token","embeddiumtokenlol"},{"refresh_expires_in",86400},
{"refresh_expires_at","9999-12-31T23:59:59.999Z"},
{"account_id",name},{"client_id","embeddiumclientidlol"},
{"application_id","embeddiumapplicationidlol"},
{"selected_account_id",name},{"id_token","embeddiumtokenlol"}
});
});
router.Get("/account/api/oauth/verify", [](HttpRequest&) {
std::string name = AccountState::Get();
return HttpResponse().Json({
{"token","embeddiumtokenlol"},
{"session_id","3c3662bcb661d6de679c636744c66b62"},
{"token_type","bearer"},{"client_id","embeddiumclientidlol"},
{"internal_client",true},{"client_service","fortnite"},
{"account_id",name},{"expires_in",28800},
{"expires_at","9999-12-02T01:12:01.100Z"},
{"auth_method","exchange_code"},{"display_name",name},
{"app","fortnite"},{"in_app_id",name},
{"device_id","embeddiumdeviceidlol"}
});
});
router.Post("/account/api/oauth/exchange", [](HttpRequest&) {
return HttpResponse().Json(Json::object());
});
router.Delete("/account/api/oauth/sessions/kill", [](HttpRequest&) {
return HttpResponse().NoContent();
});
router.Delete("/account/api/oauth/sessions/kill/*", [](HttpRequest&) {
return HttpResponse().NoContent();
});
router.Get("/account/api/epicdomains/ssodomains", [](HttpRequest&) {
return HttpResponse().Json(Json::array(
{"unrealengine.com","unrealtournament.com","fortnite.com","epicgames.com"}));
});
router.Get("/account/api/public/account/*/externalAuths", [](HttpRequest&) {
return HttpResponse().Json(Json::array());
});
router.Post("/fortnite/api/game/v2/tryPlayOnPlatform/account/*", [](HttpRequest&) {
return HttpResponse().Text("true");
});
router.Get("/sdk/v1/*", [](HttpRequest&) {
return HttpResponse().Json(JsonUtils::ParseSafe(AssetManager::GetJSON("sdkv1.json")));
});
router.Get("/epic/id/v2/sdk/accounts", [](HttpRequest&) {
std::string name = AccountState::Get();
return HttpResponse().Json(Json::array({{
{"accountId",name},{"displayName",name},
{"preferredLanguage","en"},{"cabinedMode",false},{"empty",false}
}}));
});
router.Get("/affiliate/api/public/affiliates/slug/:slug", [](HttpRequest& req) {
std::string slug = req.Param("slug");
Json sac = JsonUtils::ParseSafe(AssetManager::GetJSON("SAC.json"));
if (sac.is_array()) {
for (const auto& code : sac) {
if (!code.is_string()) continue;
std::string c = code.get<std::string>();
if (StringUtils::IEquals(slug, c))
return HttpResponse().Json({
{"id",c},{"slug",c},{"displayName",c},
{"status","ACTIVE"},{"verified",false}
});
}
}
return HttpResponse().Status(404).Json(Json::object());
});
router.Get("/friends/api/v1/*/settings", [](HttpRequest&) {
return HttpResponse().Json(Json::object());
});
router.Get("/friends/api/v1/*/blocklist", [](HttpRequest&) {
return HttpResponse().Json(Json::array());
});
router.Get("/friends/api/public/blocklist/*", [](HttpRequest&) {
return HttpResponse().Json({{"blockedUsers",Json::array()}});
});
router.Get("/friends/api/public/list/fortnite/*/recentPlayers", [](HttpRequest&) {
return HttpResponse().Json(Json::array());
});
router.Get("/friends/api/public/friends/:accountId", [](HttpRequest& req) {
std::string id = req.Param("accountId");
Json list = FriendStore::GetFriendList(id);
if (GetVer(req).season >= 7) {
Json filtered = Json::array();
for (const auto& f : list)
if (f.value("accountId","") != id) filtered.push_back(f);
return HttpResponse().Json(filtered);
}
return HttpResponse().Json(list);
});
router.Get("/friends/api/v1/:accountId/summary", [](HttpRequest& req) {
return HttpResponse().Json(FriendStore::GetFriendSummary(req.Param("accountId")));
});
router.Get("/fortnite/api/game/v2/privacy/account/:accountId", [](HttpRequest& req) {
return HttpResponse().Json(PrivacyStore::Get(req.Param("accountId")));
});
router.Post("/fortnite/api/game/v2/privacy/account/:accountId", [](HttpRequest& req) {
auto body = req.BodyJson();
bool optOut = body.is_object() ? body.value("optOutOfPublicLeaderboards",false) : false;
return HttpResponse().Json(PrivacyStore::SetOptOut(req.Param("accountId"), optOut));
});
router.Get("/fortnite/api/calendar/v1/timeline", [](HttpRequest& req) {
Config cfg = ConfigLoader::LoadEmbedded();
return HttpResponse().Json(TimelineService::BuildTimeline(GetVer(req), cfg));
});
router.Get("/fortnite/api/storefront/v2/catalog", [](HttpRequest& req) {
if (req.Header("User-Agent").find("2870186") != std::string::npos)
return HttpResponse().Status(404).NoContent();
auto ver = GetVer(req);
Json catalog = JsonUtils::DeepCopy(ItemShopService::GetCatalog());
if (ver.build >= 30.10f) {
std::string s = catalog.dump();
s = StringUtils::ReplaceAll(s,"\"Normal\"","\"Size_1_x_2\"");
catalog = JsonUtils::ParseSafe(s);
}
if (ver.build >= 30.20f) {
std::string s = catalog.dump();
s = StringUtils::ReplaceAll(s,"Game/Items/CardPacks/","SaveTheWorld/Items/CardPacks/");
catalog = JsonUtils::ParseSafe(s);
}
return HttpResponse().Json(catalog);
});
router.Get("/fortnite/api/storefront/v2/keychain", [](HttpRequest&) {
return HttpResponse().Json(JsonUtils::ParseSafe(AssetManager::GetJSON("keychain.json")));
});
router.Get("/catalog/api/shared/bulk/offers", [](HttpRequest&) {
return HttpResponse().Json(Json::object());
});
router.Get("/content/api/pages/fortnite-game", [](HttpRequest& req) {
return HttpResponse().Json(ContentService::Build(GetVer(req), req.Header("Accept-Language")));
});
router.Get("/fortnite/api/cloudstorage/system", [](HttpRequest& req) {
auto ver = GetVer(req);
if (ver.build >= 9.40f && ver.build <= 10.40f)
return HttpResponse().Status(404).NoContent();
return HttpResponse().Json(CloudStorageService::GetSystemFiles(ver));
});
router.Get("/fortnite/api/cloudstorage/system/:file", [](HttpRequest& req) {
auto ver = GetVer(req);
std::string content = CloudStorageService::GetSystemFile(req.Param("file"), ver);
if (content.empty()) return HttpResponse().Status(200).NoContent();
return HttpResponse().Status(200).Text(content).ContentType("application/octet-stream");
});
router.Get("/fortnite/api/cloudstorage/user/*/:file", [](HttpRequest& req) {
if (!StringUtils::IEquals(req.Param("file"), "clientsettings.sav"))
return HttpResponse().Status(404).Json({{"error","file not found"}});
auto ver = GetVer(req);
if (!CloudStorageService::HasClientSettings(ver.CL))
return HttpResponse().Status(200).NoContent();
std::string data = CloudStorageService::ReadClientSettings(ver.CL);
return HttpResponse().Status(200).Binary(data).ContentType("application/octet-stream");
});
router.Get("/fortnite/api/cloudstorage/user/:accountId", [](HttpRequest& req) {
auto ver = GetVer(req);
return HttpResponse().Json(CloudStorageService::GetUserFileList(req.Param("accountId"), ver.CL));
});
router.Put("/fortnite/api/cloudstorage/user/*/:file", [](HttpRequest& req) {
if (!StringUtils::IEquals(req.Param("file"), "clientsettings.sav"))
return HttpResponse().Status(404).Json({{"error","file not found"}});
auto ver = GetVer(req);
CloudStorageService::WriteClientSettings(ver.CL, req.Body());
return HttpResponse().Status(204).NoContent();
});
router.Post("*/api/v2/discovery/surface/*", [](HttpRequest&) {
auto j = JsonUtils::ParseSafe(AssetManager::GetJSON("Athena/Discovery/discovery_frontend.json"));
return HttpResponse().Json(j.value("v2", Json::object()));
});
router.Post("*/discovery/surface/*", [](HttpRequest&) {
auto j = JsonUtils::ParseSafe(AssetManager::GetJSON("Athena/Discovery/discovery_frontend.json"));
return HttpResponse().Json(j.value("v1", Json::object()));
});
router.Get("/fortnite/api/discovery/accessToken/:branch", [](HttpRequest& req) {
return HttpResponse().Json({
{"branchName",req.Param("branch")},{"appId","Fortnite"},
{"token","embeddiumtokenlol"}
});
});
router.Post("/links/api/fn/mnemonic", [](HttpRequest&) {
auto disc = JsonUtils::ParseSafe(AssetManager::GetJSON("Athena/Discovery/discovery_frontend.json"));
Json arr = Json::array();
try {
for (const auto& r : disc["v2"]["Panels"][1]["Pages"][0]["results"])
arr.push_back(r["linkData"]);
} catch (...) {}
return HttpResponse().Json(arr);
});
router.Get("/links/api/fn/mnemonic/:playlist/related", [](HttpRequest& req) {
auto disc = JsonUtils::ParseSafe(AssetManager::GetJSON("Athena/Discovery/discovery_frontend.json"));
Json resp = {{"parentLinks",Json::array()},{"links",Json::object()}};
std::string pl = req.Param("playlist");
try {
for (const auto& r : disc["v2"]["Panels"][1]["Pages"][0]["results"]) {
const auto& ld = r["linkData"];
if (ld.value("mnemonic","") == pl) resp["links"][pl] = ld;
}
} catch (...) {}
return HttpResponse().Json(resp);
});
router.Get("/links/api/fn/mnemonic/*", [](HttpRequest& req) {
auto disc = JsonUtils::ParseSafe(AssetManager::GetJSON("Athena/Discovery/discovery_frontend.json"));
std::string path(req.Path());
auto slash = path.rfind('/');
std::string mn = slash != std::string::npos ? path.substr(slash+1) : path;
try {
for (const auto& r : disc["v2"]["Panels"][1]["Pages"][0]["results"])
if (r["linkData"].value("mnemonic","") == mn)
return HttpResponse().Json(r["linkData"]);
} catch (...) {}
return HttpResponse().Json(Json::object());
});
router.Post("/api/v1/links/lock-status/:accountId/check", [](HttpRequest&) {
return HttpResponse().Json({{"results",Json::array()},{"hasMore",false}});
});
router.Get("/fortnite/api/matchmaking/session/findPlayer/*", [](HttpRequest&) {
return HttpResponse().Status(200).NoContent();
});
router.Get("/fortnite/api/game/v2/matchmakingservice/ticket/player/*", [](HttpRequest& req) {
std::string bucketId = req.Query("bucketId");
std::string buildId;
auto colon = bucketId.find(':');
if (colon != std::string::npos) buildId = bucketId.substr(0, colon);
HttpResponse resp;
resp.SetCookie("currentbuildUniqueId", buildId, "/");
resp.Json({
{"serviceUrl","ws://127.0.0.1"},{"ticketType","mms-player"},
{"payload","69="},{"signature","420="}
});
return resp;
});
router.Get("/fortnite/api/game/v2/matchmaking/account/:accountId/session/:sessionId",
[](HttpRequest& req) {
return HttpResponse().Json({
{"accountId",req.Param("accountId")},
{"sessionId",req.Param("sessionId")},
{"key","AOJEv8uTFmUh7XM2328kq9rlAzeQ5xzWzPIiyKn2s7s="}
});
});
router.Get("/fortnite/api/matchmaking/session/:session_id", [](HttpRequest& req) {
Config cfg = ConfigLoader::LoadEmbedded();
return HttpResponse().Json({
{"id",req.Param("session_id")},
{"ownerId",StringUtils::ToUpper(UuidGenerator::MakeIDNoDashes())},
{"ownerName","[DS]fortnite-liveeugcec1c2e30ubrcore0a-z8hj-1968"},
{"serverName","[DS]fortnite-liveeugcec1c2e30ubrcore0a-z8hj-1968"},
{"serverAddress",cfg.gameServerIp},{"serverPort",cfg.gameServerPort},
{"maxPublicPlayers",220},{"openPublicPlayers",175},
{"maxPrivatePlayers",0},{"openPrivatePlayers",0},
{"attributes",{
{"REGION_s","EU"},{"GAMEMODE_s","FORTATHENA"},
{"ALLOWBROADCASTING_b",true},{"SUBREGION_s","GB"},
{"DCID_s","FORTNITE-LIVEEUGCEC1C2E30UBRCORE0A-14840880"},
{"tenant_s","Fortnite"},{"MATCHMAKINGPOOL_s","Any"},
{"STORMSHIELDDEFENSETYPE_i",0},{"HOTFIXVERSION_i",0},
{"PLAYLISTNAME_s","Playlist_DefaultSolo"},
{"SESSIONKEY_s",StringUtils::ToUpper(UuidGenerator::MakeIDNoDashes())},
{"TENANT_s","Fortnite"},{"BEACONPORT_i",15009}
}},
{"publicPlayers",Json::array()},{"privatePlayers",Json::array()},
{"totalPlayers",45},{"allowJoinInProgress",false},
{"shouldAdvertise",false},{"isDedicated",false},
{"usesStats",false},{"allowInvites",false},{"usesPresence",false},
{"allowJoinViaPresence",true},{"allowJoinViaPresenceFriendsOnly",false},
{"buildUniqueId", req.Cookie("currentbuildUniqueId").empty()
? std::string("0") : req.Cookie("currentbuildUniqueId")},
{"lastUpdated",NowIso()},{"started",false}
});
});
router.Post("/fortnite/api/matchmaking/session/*/join", [](HttpRequest&) {
return HttpResponse().Status(204).NoContent();
});
router.Post("/fortnite/api/matchmaking/session/matchMakingRequest", [](HttpRequest&) {
return HttpResponse().Json(Json::array());
});
router.Get("/party/api/v1/Fortnite/user/*", [](HttpRequest&) {
return HttpResponse().Json({
{"current",Json::array()},{"pending",Json::array()},
{"invites",Json::array()},{"pings",Json::array()}
});
});
router.Post("/party/api/v1/Fortnite/parties", [](HttpRequest& req) {
auto body = req.BodyJson();
if (!body.is_object() || !body.contains("join_info") ||
!body["join_info"].contains("connection"))
return HttpResponse().Json(Json::object());
std::string connId = body["join_info"]["connection"].value("id","");
std::string acctId = connId;
auto atProd = acctId.find("@prod");
if (atProd != std::string::npos) acctId = acctId.substr(0, atProd);
std::string now = NowIso();
Json cfg = body.value("config",Json::object());
cfg["type"] = "DEFAULT"; cfg["discoverability"] = "ALL";
cfg["sub_type"] = "default"; cfg["invite_ttl"] = 14400; cfg["intention_ttl"] = 60;
return HttpResponse().Json({
{"id",UuidGenerator::MakeIDNoDashes()},
{"created_at",now},{"updated_at",now},{"config",cfg},
{"members",Json::array({{
{"account_id",acctId},
{"meta",body["join_info"].value("meta",Json::object())},
{"connections",Json::array({{
{"id",connId},{"connected_at",now},{"updated_at",now},
{"yield_leadership",false},
{"meta",body["join_info"]["connection"].value("meta",Json::object())}
}})},
{"revision",0},{"updated_at",now},{"joined_at",now},{"role","CAPTAIN"}
}})},
{"applicants",Json::array()},
{"meta",body.value("meta",Json::object())},
{"invites",Json::array()},{"revision",0},{"intentions",Json::array()}
});
});
router.Patch("/party/api/v1/Fortnite/parties/:partyId", [](HttpRequest& req) {
auto body = req.BodyJson();
return HttpResponse().Json({
{"id",req.Param("partyId")},
{"config",body.is_object() ? body.value("config",Json::object()) : Json::object()},
{"revision",0},{"updated_at",NowIso()}
});
});
router.Post("/party/api/v1/Fortnite/parties/:partyId/members/:accountId/join",
[](HttpRequest& req) {
std::string now = NowIso();
return HttpResponse().Json({
{"status","JOINED"},{"party_id",req.Param("partyId")},
{"account_id",req.Param("accountId")},
{"joined_at",now},{"updated_at",now}
});
});
router.Delete("/party/api/v1/Fortnite/parties/:partyId/members/:accountId",
[](HttpRequest&) {
return HttpResponse().Status(204).NoContent();
});
router.Delete("/party/api/v1/Fortnite/parties/:partyId", [](HttpRequest&) {
return HttpResponse().Status(204).NoContent();
});
router.Post("/party/api/v1/Fortnite/user/:accountId/pings/*", [](HttpRequest&) {
return HttpResponse().Status(204).NoContent();
});
router.Delete("/party/api/v1/Fortnite/user/:accountId/pings/*", [](HttpRequest&) {
return HttpResponse().Status(204).NoContent();
});
router.Get("/fortnite/api/game/v2/world/info", [](HttpRequest& req) {
return HttpResponse().Json(TheaterService::Build(GetVer(req)));
});
router.Get("/fortnite/api/game/v2/br-inventory/account/*", [](HttpRequest&) {
return HttpResponse().Json({{"stash",{{"globalcash",9999999}}}});
});
router.Get("/fortnite/api/game/v2/enabled_features", [](HttpRequest&) {
return HttpResponse().Json(Json::array());
});
router.Get("/fortnite/api/receipts/v1/account/*/receipts", [](HttpRequest&) {
return HttpResponse().Json(Json::array());
});
router.Get("/fortnite/api/game/v2/purchased-limit-offers", [](HttpRequest&) {
return HttpResponse().Json({{"purchases",Json::array()}});
});
router.Get("/fortnite/api/game/v2/profiletoken/verify/*", [](HttpRequest&) {
return HttpResponse().Status(200).NoContent();
});
router.Get("/fortnite/api/game/v2/tournaments/*", [](HttpRequest&) {
return HttpResponse().Json(
JsonUtils::ParseSafe(AssetManager::GetJSON("Athena/Tournament/tournament.json")));
});
router.Get("/fortnite/api/game/v2/tournamentandhistory/*", [](HttpRequest&) {
return HttpResponse().Json(
JsonUtils::ParseSafe(AssetManager::GetJSON("Athena/Tournament/tournamentandhistory.json")));
});
router.Get("/fortnite/api/game/v2/settings", [](HttpRequest&) {
return HttpResponse().Json(
JsonUtils::ParseSafe(AssetManager::GetJSON("epic-settings.json")));
});
router.Get("/socialban/api/public/v1/*", [](HttpRequest&) {
return HttpResponse().Json({{"bans",Json::array()},{"moderation",Json::array()}});
});
router.Get("/fortnite/api/game/v2/leaderboards/*", [](HttpRequest&) {
return HttpResponse().Json({{"entries",Json::array()}});
});
router.Get("/fortnite/api/statsv2/*", [](HttpRequest&) {
return HttpResponse().Json({
{"startTime",0},{"endTime",0},
{"stats",Json::object()},{"accountId",AccountState::Get()}
});
});
router.Post("/fortnite/api/statsv2/query", [](HttpRequest&) {
return HttpResponse().Json(Json::array());
});
router.Get("/Builds/Fortnite/Content/CloudDir/embeddiumServer.manifest", [](HttpRequest&) {
auto data = AssetManager::GetBinary("CloudDir/embeddiumServer.manifest");
if (data.empty()) return HttpResponse().Status(404).NoContent();
std::string body(reinterpret_cast<const char*>(data.data()), data.size());
return HttpResponse().Binary(body).ContentType("application/octet-stream");
});
router.Get("/Builds/Fortnite/Content/CloudDir/embeddiumServer.chunk", [](HttpRequest&) {
auto data = AssetManager::GetBinary("CloudDir/embeddiumServer.chunk");
if (data.empty()) return HttpResponse().Status(404).NoContent();
std::string body(reinterpret_cast<const char*>(data.data()), data.size());
return HttpResponse().Binary(body).ContentType("application/octet-stream");
});
router.Get("/Builds/Fortnite/Content/CloudDir/*", [](HttpRequest&) {
auto text = AssetManager::GetText("CloudDir/Full.ini");
if (text.empty()) return HttpResponse().Status(404).NoContent();
return HttpResponse().Text(std::string(text)).ContentType("application/octet-stream");
});
McpRoutes::RegisterAll(router);
}
} // namespace Embeddium::Routes
+9
View File
@@ -0,0 +1,9 @@
#pragma once
#include "../HTTP/Router.h"
namespace Embeddium::Routes {
// Called from Initializer::Run() — registers all HTTP routes on the router
void RegisterAll(Router& router);
} // namespace Embeddium::Routes
+89
View File
@@ -0,0 +1,89 @@
#include "Base64.h"
namespace Embeddium::Base64 {
namespace {
constexpr char kTable[] =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
constexpr int kDecodeTable[256] = {
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,62,-1,-1,-1,63,
52,53,54,55,56,57,58,59,60,61,-1,-1,-1,-1,-1,-1,
-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,
15,16,17,18,19,20,21,22,23,24,25,-1,-1,-1,-1,-1,
-1,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,
41,42,43,44,45,46,47,48,49,50,51,-1,-1,-1,-1,-1,
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
};
}
std::string Encode(std::string_view input) {
std::string result;
result.reserve(((input.size() + 2) / 3) * 4);
size_t i = 0;
while (i + 3 <= input.size()) {
uint32_t val = (static_cast<uint8_t>(input[i]) << 16) |
(static_cast<uint8_t>(input[i+1]) << 8) |
static_cast<uint8_t>(input[i+2]);
result += kTable[(val >> 18) & 0x3F];
result += kTable[(val >> 12) & 0x3F];
result += kTable[(val >> 6) & 0x3F];
result += kTable[(val ) & 0x3F];
i += 3;
}
if (i + 1 == input.size()) {
uint32_t val = static_cast<uint8_t>(input[i]) << 16;
result += kTable[(val >> 18) & 0x3F];
result += kTable[(val >> 12) & 0x3F];
result += '=';
result += '=';
} else if (i + 2 == input.size()) {
uint32_t val = (static_cast<uint8_t>(input[i]) << 16) |
(static_cast<uint8_t>(input[i+1]) << 8);
result += kTable[(val >> 18) & 0x3F];
result += kTable[(val >> 12) & 0x3F];
result += kTable[(val >> 6) & 0x3F];
result += '=';
}
return result;
}
std::vector<uint8_t> DecodeBytes(std::string_view input) {
std::vector<uint8_t> result;
result.reserve((input.size() / 4) * 3);
uint32_t val = 0;
int bits = 0;
for (char c : input) {
if (c == '=') break;
int d = kDecodeTable[static_cast<uint8_t>(c)];
if (d < 0) continue; // skip whitespace / invalid
val = (val << 6) | static_cast<uint32_t>(d);
bits += 6;
if (bits >= 8) {
bits -= 8;
result.push_back(static_cast<uint8_t>((val >> bits) & 0xFF));
}
}
return result;
}
std::string Decode(std::string_view input) {
auto bytes = DecodeBytes(input);
return std::string(reinterpret_cast<const char*>(bytes.data()), bytes.size());
}
} // namespace Embeddium::Base64
+23
View File
@@ -0,0 +1,23 @@
#pragma once
#include <string>
#include <string_view>
#include <vector>
#include <cstdint>
namespace Embeddium {
// ─────────────────────────────────────────────────────────────────────────────
// Base64
// Standard RFC 4648 base64 encode/decode.
// Used for XMPP SASL PLAIN authentication.
// ─────────────────────────────────────────────────────────────────────────────
namespace Base64 {
std::string Encode(std::string_view input);
std::string Decode(std::string_view input);
// Decode to raw bytes
std::vector<uint8_t> DecodeBytes(std::string_view input);
} // namespace Base64
} // namespace Embeddium
+69
View File
@@ -0,0 +1,69 @@
#include "HashUtils.h"
#include <Windows.h>
#include <bcrypt.h>
#include <cstdio>
#include <stdexcept>
#pragma comment(lib, "Bcrypt.lib")
namespace Embeddium::HashUtils {
namespace {
// Generic BCrypt hash helper.
std::vector<uint8_t> BCryptHash(LPCWSTR algorithm,
const uint8_t* data, size_t dataLen) {
BCRYPT_ALG_HANDLE hAlg = nullptr;
BCRYPT_HASH_HANDLE hHash = nullptr;
if (!BCRYPT_SUCCESS(BCryptOpenAlgorithmProvider(&hAlg, algorithm, nullptr, 0)))
return {};
DWORD hashLen = 0, cbData = 0;
BCryptGetProperty(hAlg, BCRYPT_HASH_LENGTH,
reinterpret_cast<PUCHAR>(&hashLen), sizeof(DWORD), &cbData, 0);
std::vector<uint8_t> result(hashLen);
if (BCRYPT_SUCCESS(BCryptCreateHash(hAlg, &hHash, nullptr, 0, nullptr, 0, 0))) {
BCryptHashData(hHash, const_cast<PUCHAR>(data), static_cast<ULONG>(dataLen), 0);
BCryptFinishHash(hHash, result.data(), hashLen, 0);
BCryptDestroyHash(hHash);
}
BCryptCloseAlgorithmProvider(hAlg, 0);
return result;
}
std::string ToHex(const std::vector<uint8_t>& bytes) {
std::string result;
result.resize(bytes.size() * 2);
for (size_t i = 0; i < bytes.size(); ++i) {
snprintf(&result[i * 2], 3, "%02x", bytes[i]);
}
return result;
}
}
std::string SHA1Hex(std::string_view data) {
return ToHex(BCryptHash(BCRYPT_SHA1_ALGORITHM,
reinterpret_cast<const uint8_t*>(data.data()),
data.size()));
}
std::string SHA1Hex(const std::vector<uint8_t>& data) {
return ToHex(BCryptHash(BCRYPT_SHA1_ALGORITHM, data.data(), data.size()));
}
std::string SHA256Hex(std::string_view data) {
return ToHex(BCryptHash(BCRYPT_SHA256_ALGORITHM,
reinterpret_cast<const uint8_t*>(data.data()),
data.size()));
}
std::string SHA256Hex(const std::vector<uint8_t>& data) {
return ToHex(BCryptHash(BCRYPT_SHA256_ALGORITHM, data.data(), data.size()));
}
} // namespace Embeddium::HashUtils
+25
View File
@@ -0,0 +1,25 @@
#pragma once
#include <string>
#include <string_view>
#include <vector>
#include <cstdint>
namespace Embeddium {
// ─────────────────────────────────────────────────────────────────────────────
// HashUtils
// SHA1 and SHA256 via Windows BCrypt API.
// Used by CloudStorageService to compute file integrity hashes.
// ─────────────────────────────────────────────────────────────────────────────
namespace HashUtils {
// Returns lowercase hex SHA-1 of data
std::string SHA1Hex(std::string_view data);
std::string SHA1Hex(const std::vector<uint8_t>& data);
// Returns lowercase hex SHA-256 of data
std::string SHA256Hex(std::string_view data);
std::string SHA256Hex(const std::vector<uint8_t>& data);
} // namespace HashUtils
} // namespace Embeddium
+78
View File
@@ -0,0 +1,78 @@
#pragma once
// ─────────────────────────────────────────────────────────────────────────────
// JsonUtils.h
// Convenience header that pulls in nlohmann/json and provides type aliases
// and small helpers used throughout the codebase.
// ─────────────────────────────────────────────────────────────────────────────
// Suppress noisy warnings from the nlohmann header on MSVC
#pragma warning(push)
#pragma warning(disable: 4127 4244 4996)
#include "../vendor/nlohmann/json.hpp"
#pragma warning(pop)
namespace Embeddium {
using Json = nlohmann::json;
namespace JsonUtils {
// Parse JSON string; returns empty object on failure (never throws)
inline Json ParseSafe(std::string_view text) {
try {
return Json::parse(text);
} catch (...) {
return Json::object();
}
}
// Deep-copy a JSON value (equivalent to JSON.parse(JSON.stringify(obj)) in JS)
inline Json DeepCopy(const Json& j) {
return Json::parse(j.dump());
}
// Get string value with default
inline std::string GetStr(const Json& j, std::string_view key,
std::string_view def = "") {
if (j.is_object() && j.contains(key)) {
const auto& v = j.at(std::string(key));
if (v.is_string()) return v.get<std::string>();
}
return std::string(def);
}
// Get int value with default
inline int GetInt(const Json& j, std::string_view key, int def = 0) {
if (j.is_object() && j.contains(key)) {
const auto& v = j.at(std::string(key));
if (v.is_number_integer()) return v.get<int>();
if (v.is_number()) return static_cast<int>(v.get<double>());
}
return def;
}
// Get bool value with default
inline bool GetBool(const Json& j, std::string_view key, bool def = false) {
if (j.is_object() && j.contains(key)) {
const auto& v = j.at(std::string(key));
if (v.is_boolean()) return v.get<bool>();
if (v.is_string()) {
std::string s = v.get<std::string>();
return s == "true" || s == "1";
}
}
return def;
}
// Get double value with default
inline double GetDouble(const Json& j, std::string_view key, double def = 0.0) {
if (j.is_object() && j.contains(key)) {
const auto& v = j.at(std::string(key));
if (v.is_number()) return v.get<double>();
}
return def;
}
} // namespace JsonUtils
} // namespace Embeddium
+138
View File
@@ -0,0 +1,138 @@
#include "StringUtils.h"
#include <cctype>
#include <stdexcept>
namespace Embeddium::StringUtils {
bool IEquals(std::string_view a, std::string_view b) {
if (a.size() != b.size()) return false;
for (size_t i = 0; i < a.size(); ++i) {
if (std::tolower((unsigned char)a[i]) != std::tolower((unsigned char)b[i]))
return false;
}
return true;
}
bool StartsWith(std::string_view str, std::string_view prefix) {
return str.size() >= prefix.size() &&
str.substr(0, prefix.size()) == prefix;
}
bool EndsWith(std::string_view str, std::string_view suffix) {
return str.size() >= suffix.size() &&
str.substr(str.size() - suffix.size()) == suffix;
}
bool Contains(std::string_view str, std::string_view needle) {
return str.find(needle) != std::string_view::npos;
}
bool ContainsI(std::string_view str, std::string_view needle) {
if (needle.empty()) return true;
if (needle.size() > str.size()) return false;
std::string haystack = ToLower(str);
std::string n = ToLower(needle);
return haystack.find(n) != std::string::npos;
}
std::string ToLower(std::string_view str) {
std::string result(str);
std::transform(result.begin(), result.end(), result.begin(),
[](unsigned char c) { return (char)std::tolower(c); });
return result;
}
std::string ToUpper(std::string_view str) {
std::string result(str);
std::transform(result.begin(), result.end(), result.begin(),
[](unsigned char c) { return (char)std::toupper(c); });
return result;
}
std::string ReplaceAll(std::string subject, std::string_view from, std::string_view to) {
if (from.empty()) return subject;
size_t pos = 0;
while ((pos = subject.find(from, pos)) != std::string::npos) {
subject.replace(pos, from.size(), to);
pos += to.size();
}
return subject;
}
std::string ReplaceAllI(std::string subject, std::string_view from, std::string_view to) {
if (from.empty()) return subject;
std::string lower_subject = ToLower(subject);
std::string lower_from = ToLower(from);
std::string result;
size_t pos = 0;
while (true) {
size_t found = lower_subject.find(lower_from, pos);
if (found == std::string::npos) {
result += subject.substr(pos);
break;
}
result += subject.substr(pos, found - pos);
result += to;
pos = found + from.size();
}
return result;
}
std::vector<std::string> Split(std::string_view str, char delimiter) {
std::vector<std::string> result;
size_t start = 0;
size_t pos;
while ((pos = str.find(delimiter, start)) != std::string_view::npos) {
result.emplace_back(str.substr(start, pos - start));
start = pos + 1;
}
result.emplace_back(str.substr(start));
return result;
}
std::string Trim(std::string_view str) {
size_t start = 0;
while (start < str.size() && std::isspace((unsigned char)str[start])) ++start;
size_t end = str.size();
while (end > start && std::isspace((unsigned char)str[end - 1])) --end;
return std::string(str.substr(start, end - start));
}
std::string Join(const std::vector<std::string>& parts, std::string_view separator) {
std::string result;
for (size_t i = 0; i < parts.size(); ++i) {
if (i > 0) result += separator;
result += parts[i];
}
return result;
}
std::string UrlDecode(std::string_view encoded) {
std::string result;
result.reserve(encoded.size());
for (size_t i = 0; i < encoded.size(); ++i) {
if (encoded[i] == '%' && i + 2 < encoded.size()) {
char hi = encoded[i + 1];
char lo = encoded[i + 2];
auto hexVal = [](char c) -> int {
if (c >= '0' && c <= '9') return c - '0';
if (c >= 'a' && c <= 'f') return c - 'a' + 10;
if (c >= 'A' && c <= 'F') return c - 'A' + 10;
return -1;
};
int h = hexVal(hi), l = hexVal(lo);
if (h >= 0 && l >= 0) {
result += static_cast<char>((h << 4) | l);
i += 2;
continue;
}
} else if (encoded[i] == '+') {
result += ' ';
continue;
}
result += encoded[i];
}
return result;
}
} // namespace Embeddium::StringUtils
+56
View File
@@ -0,0 +1,56 @@
#pragma once
#include <string>
#include <string_view>
#include <vector>
#include <algorithm>
#include <sstream>
namespace Embeddium {
// ─────────────────────────────────────────────────────────────────────────────
// StringUtils
// Collection of stateless string helpers used throughout the codebase.
// ─────────────────────────────────────────────────────────────────────────────
namespace StringUtils {
// Case-insensitive comparison
bool IEquals(std::string_view a, std::string_view b);
// Checks whether 'str' starts with 'prefix' (case-sensitive)
bool StartsWith(std::string_view str, std::string_view prefix);
// Checks whether 'str' ends with 'suffix' (case-sensitive)
bool EndsWith(std::string_view str, std::string_view suffix);
// Checks whether 'str' contains 'needle' (case-sensitive)
bool Contains(std::string_view str, std::string_view needle);
// Case-insensitive Contains
bool ContainsI(std::string_view str, std::string_view needle);
// Returns a lowercased copy of str
std::string ToLower(std::string_view str);
// Returns an uppercased copy of str
std::string ToUpper(std::string_view str);
// Replace all occurrences of 'from' with 'to' in subject (returns new string)
std::string ReplaceAll(std::string subject, std::string_view from, std::string_view to);
// Case-insensitive ReplaceAll
std::string ReplaceAllI(std::string subject, std::string_view from, std::string_view to);
// Split string by delimiter
std::vector<std::string> Split(std::string_view str, char delimiter);
// Trim leading and trailing whitespace
std::string Trim(std::string_view str);
// Join a vector of strings with a separator
std::string Join(const std::vector<std::string>& parts, std::string_view separator);
// Decode a URL-encoded query string parameter
std::string UrlDecode(std::string_view encoded);
} // namespace StringUtils
} // namespace Embeddium
+77
View File
@@ -0,0 +1,77 @@
#include "UuidGenerator.h"
#include <Windows.h>
#include <bcrypt.h>
#include <array>
#include <cstdint>
#include <cstdio>
#pragma comment(lib, "Bcrypt.lib")
namespace Embeddium::UuidGenerator {
namespace {
// Fills buffer with cryptographically random bytes using BCrypt.
bool RandomBytes(void* buf, size_t len) {
BCRYPT_ALG_HANDLE hAlg = nullptr;
if (!BCRYPT_SUCCESS(BCryptOpenAlgorithmProvider(&hAlg, BCRYPT_RNG_ALGORITHM, nullptr, 0)))
return false;
bool ok = BCRYPT_SUCCESS(BCryptGenRandom(hAlg, static_cast<PUCHAR>(buf),
static_cast<ULONG>(len), 0));
BCryptCloseAlgorithmProvider(hAlg, 0);
return ok;
}
}
std::string MakeID() {
std::array<uint8_t, 16> bytes{};
if (!RandomBytes(bytes.data(), bytes.size())) {
// Fallback: use system tick count seeded xorshift (not crypto-safe but acceptable)
uint64_t seed = static_cast<uint64_t>(GetTickCount64());
for (size_t i = 0; i < 16; ++i) {
seed ^= seed << 13;
seed ^= seed >> 7;
seed ^= seed << 17;
bytes[i] = static_cast<uint8_t>(seed & 0xFF);
}
}
// Set UUID v4 version bits
bytes[6] = (bytes[6] & 0x0F) | 0x40; // version 4
bytes[8] = (bytes[8] & 0x3F) | 0x80; // variant bits
char buf[37];
snprintf(buf, sizeof(buf),
"%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x",
bytes[0], bytes[1], bytes[2], bytes[3],
bytes[4], bytes[5],
bytes[6], bytes[7],
bytes[8], bytes[9],
bytes[10], bytes[11], bytes[12], bytes[13], bytes[14], bytes[15]);
return buf;
}
std::string MakeIDNoDashes() {
std::array<uint8_t, 16> bytes{};
if (!RandomBytes(bytes.data(), bytes.size())) {
uint64_t seed = static_cast<uint64_t>(GetTickCount64());
for (size_t i = 0; i < 16; ++i) {
seed ^= seed << 13; seed ^= seed >> 7; seed ^= seed << 17;
bytes[i] = static_cast<uint8_t>(seed & 0xFF);
}
}
bytes[6] = (bytes[6] & 0x0F) | 0x40;
bytes[8] = (bytes[8] & 0x3F) | 0x80;
char buf[33];
snprintf(buf, sizeof(buf),
"%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X",
bytes[0], bytes[1], bytes[2], bytes[3],
bytes[4], bytes[5], bytes[6], bytes[7],
bytes[8], bytes[9], bytes[10], bytes[11],
bytes[12], bytes[13], bytes[14], bytes[15]);
return buf;
}
} // namespace Embeddium::UuidGenerator
+22
View File
@@ -0,0 +1,22 @@
#pragma once
#include <string>
namespace Embeddium {
// ─────────────────────────────────────────────────────────────────────────────
// UuidGenerator
// Generates UUID v4 strings using Windows CryptGenRandom.
// Format: xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx
// ─────────────────────────────────────────────────────────────────────────────
namespace UuidGenerator {
// Returns a random UUID v4 string (lowercase hex with dashes)
// e.g. "550e8400-e29b-41d4-a716-446655440000"
std::string MakeID();
// Returns a UUID v4 with dashes removed (32 hex chars, uppercase)
// e.g. "550E8400E29B41D4A716446655440000"
std::string MakeIDNoDashes();
} // namespace UuidGenerator
} // namespace Embeddium
+210
View File
@@ -0,0 +1,210 @@
#include "../Network/WebSocketServer.h"
#include "../Backend/AccountState.h"
#include "../Core/Log.h"
#include "../Utilities/UuidGenerator.h"
#include <string>
#include <string_view>
#include <memory>
#include <functional>
#include <algorithm>
#include <cctype>
// XMPP-over-WebSocket (RFC 7395) with SASL PLAIN and resource binding.
//
// Flow:
// 1. Client → <open/> (stream open)
// 2. Server → <open/> + SASL features
// 3. Client → <auth>b64</auth>
// 4. Server → <success/>
// 5. Client → <open/> (post-auth stream)
// 6. Server → <open/> + bind/session features
// 7. Client → <iq> bind / session
// 8. Server → <iq result> with bound JID
// 9. Client ↔ Server: roster, presence, pings, messages
namespace Embeddium::XmppServer {
namespace {
constexpr std::string_view kXmppDomain = "prod.ol.epicgames.com";
static bool Contains(std::string_view haystack, std::string_view needle) {
if (needle.size() > haystack.size()) return false;
auto it = std::search(haystack.begin(), haystack.end(),
needle.begin(), needle.end(),
[](char a, char b) {
return ::tolower((unsigned char)a) == ::tolower((unsigned char)b);
});
return it != haystack.end();
}
static std::string AttrValue(std::string_view xml, std::string_view attr) {
std::string search(attr); search += '=';
auto pos = xml.find(search);
if (pos == std::string_view::npos) return {};
pos += search.size();
if (pos >= xml.size()) return {};
char quote = xml[pos];
if (quote != '"' && quote != '\'') return {};
++pos;
auto end = xml.find(quote, pos);
if (end == std::string_view::npos) return {};
return std::string(xml.substr(pos, end - pos));
}
static std::string InnerText(std::string_view xml) {
auto start = xml.find('>');
if (start == std::string_view::npos) return {};
++start;
auto end = xml.rfind('<');
if (end == std::string_view::npos || end < start) return {};
return std::string(xml.substr(start, end - start));
}
struct XmppSession {
WebSocketSession::Ptr ws;
std::string accountId;
std::string resource;
std::string jid;
enum class Phase { Open, Auth, Bound } phase = Phase::Open;
explicit XmppSession(WebSocketSession::Ptr s) : ws(std::move(s)) {}
void Send(std::string xml) { ws->SendText(std::move(xml)); }
void SendOpen(bool withFeatures) {
std::string sid = UuidGenerator::MakeIDNoDashes();
Send("<open xmlns=\"urn:ietf:params:xml:ns:xmpp-framing\""
" from=\"" + std::string(kXmppDomain) + "\""
" id=\"" + sid + "\""
" version=\"1.0\" xml:lang=\"en\"/>");
if (withFeatures) {
Send("<stream:features>"
"<mechanisms xmlns=\"urn:ietf:params:xml:ns:xmpp-sasl\">"
"<mechanism>PLAIN</mechanism>"
"</mechanisms>"
"</stream:features>");
} else {
Send("<stream:features>"
"<bind xmlns=\"urn:ietf:params:xml:ns:xmpp-bind\"/>"
"<session xmlns=\"urn:ietf:params:xml:ns:xmpp-session\"/>"
"</stream:features>");
}
}
void SendSuccess() { Send("<success xmlns=\"urn:ietf:params:xml:ns:xmpp-sasl\"/>"); }
void SendBindResult(const std::string& iqId) {
Send("<iq xmlns=\"jabber:client\" type=\"result\" id=\"" + iqId + "\">"
"<bind xmlns=\"urn:ietf:params:xml:ns:xmpp-bind\">"
"<jid>" + jid + "</jid>"
"</bind></iq>");
}
void SendSessionResult(const std::string& iqId) {
Send("<iq xmlns=\"jabber:client\" type=\"result\""
" to=\"" + jid + "\" id=\"" + iqId + "\"/>");
}
void SendRosterResult(const std::string& iqId) {
Send("<iq xmlns=\"jabber:client\" type=\"result\""
" to=\"" + jid + "\" id=\"" + iqId + "\">"
"<query xmlns=\"jabber:iq:roster\"/></iq>");
}
void SendPresenceAck() {
Send("<presence xmlns=\"jabber:client\""
" to=\"" + jid + "\""
" from=\"" + jid + "\" type=\"available\"/>");
}
void SendPingResult(const std::string& iqId, const std::string& from) {
Send("<iq xmlns=\"jabber:client\" type=\"result\""
" to=\"" + from + "\" id=\"" + iqId + "\"/>");
}
void OnMessage(const std::string& msg) {
std::string_view xml(msg);
while (!xml.empty() && std::isspace((unsigned char)xml.front())) xml.remove_prefix(1);
if (xml.empty()) return;
if (Contains(xml, "<open")) {
if (phase == Phase::Open) {
Log::Debug("XMPP: stream open (phase=Open)");
SendOpen(true);
} else if (phase == Phase::Auth) {
Log::Debug("XMPP: stream open (phase=Auth, advancing to Bound)");
SendOpen(false);
phase = Phase::Bound;
}
return;
}
if (Contains(xml, "<auth")) {
accountId = AccountState::Get();
if (accountId.empty()) accountId = "embeddium";
phase = Phase::Auth;
Log::Debug("XMPP: auth, accountId=" + accountId);
SendSuccess();
return;
}
if (Contains(xml, "<iq")) {
std::string iqId = AttrValue(xml, "id");
std::string from = AttrValue(xml, "from");
if (Contains(xml, "xmpp-bind") ||
Contains(xml, "xmlns=\"urn:ietf:params:xml:ns:xmpp-bind\"")) {
resource = InnerText(xml);
if (resource.empty()) resource = UuidGenerator::MakeIDNoDashes();
jid = accountId + "@" + std::string(kXmppDomain) + "/" + resource;
Log::Debug("XMPP: bind, jid=" + jid);
SendBindResult(iqId);
return;
}
if (Contains(xml, "xmpp-session") ||
Contains(xml, "xmlns=\"urn:ietf:params:xml:ns:xmpp-session\"")) {
Log::Debug("XMPP: session established");
SendSessionResult(iqId);
return;
}
if (Contains(xml, "jabber:iq:roster")) {
SendRosterResult(iqId);
return;
}
if (Contains(xml, "urn:ietf:params:xml:ns:xmpp-ping") ||
Contains(xml, "jabber:client\" type=\"get\"")) {
SendPingResult(iqId, from.empty() ? jid : from);
return;
}
if (!iqId.empty())
Send("<iq xmlns=\"jabber:client\" type=\"result\" id=\"" + iqId + "\"/>");
return;
}
if (Contains(xml, "<presence")) {
SendPresenceAck();
return;
}
}
};
}
void OnSession(WebSocketSession::Ptr session) {
Log::Debug("XMPP: session #" + std::to_string(session->Id()) + " connected");
auto state = std::make_shared<XmppSession>(session);
session->SetMessageHandler([state](std::string_view msg) {
state->OnMessage(std::string(msg));
});
session->SetCloseHandler([state]() { (void)state; });
}
} // namespace Embeddium::XmppServer
+5
View File
@@ -0,0 +1,5 @@
{
"name": "embeddium",
"version": "1.0.0",
"dependencies": []
}
+186
View File
@@ -0,0 +1,186 @@
/*
* MinHook - The Minimalistic API Hooking Library for x64/x86
* Copyright (C) 2009-2017 Tsuda Kageyu.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER
* OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#pragma once
#if !(defined _M_IX86) && !(defined _M_X64) && !(defined __i386__) && !(defined __x86_64__)
#error MinHook supports only x86 and x64 systems.
#endif
#include <windows.h>
// MinHook Error Codes.
typedef enum MH_STATUS
{
// Unknown error. Should not be returned.
MH_UNKNOWN = -1,
// Successful.
MH_OK = 0,
// MinHook is already initialized.
MH_ERROR_ALREADY_INITIALIZED,
// MinHook is not initialized yet, or already uninitialized.
MH_ERROR_NOT_INITIALIZED,
// The hook for the specified target function is already created.
MH_ERROR_ALREADY_CREATED,
// The hook for the specified target function is not created yet.
MH_ERROR_NOT_CREATED,
// The hook for the specified target function is already enabled.
MH_ERROR_ENABLED,
// The hook for the specified target function is not enabled yet, or already
// disabled.
MH_ERROR_DISABLED,
// The specified pointer is invalid. It points the address of non-allocated
// and/or non-executable region.
MH_ERROR_NOT_EXECUTABLE,
// The specified target function cannot be hooked.
MH_ERROR_UNSUPPORTED_FUNCTION,
// Failed to allocate memory.
MH_ERROR_MEMORY_ALLOC,
// Failed to change the memory protection.
MH_ERROR_MEMORY_PROTECT,
// The specified module is not loaded.
MH_ERROR_MODULE_NOT_FOUND,
// The specified function is not found.
MH_ERROR_FUNCTION_NOT_FOUND
}
MH_STATUS;
// Can be passed as a parameter to MH_EnableHook, MH_DisableHook,
// MH_QueueEnableHook or MH_QueueDisableHook.
#define MH_ALL_HOOKS NULL
#ifdef __cplusplus
extern "C" {
#endif
// Initialize the MinHook library. You must call this function EXACTLY ONCE
// at the beginning of your program.
MH_STATUS WINAPI MH_Initialize(VOID);
// Uninitialize the MinHook library. You must call this function EXACTLY
// ONCE at the end of your program.
MH_STATUS WINAPI MH_Uninitialize(VOID);
// Creates a Hook for the specified target function, in disabled state.
// Parameters:
// pTarget [in] A pointer to the target function, which will be
// overridden by the detour function.
// pDetour [in] A pointer to the detour function, which will override
// the target function.
// ppOriginal [out] A pointer to the trampoline function, which will be
// used to call the original target function.
// This parameter can be NULL.
MH_STATUS WINAPI MH_CreateHook(LPVOID pTarget, LPVOID pDetour, LPVOID *ppOriginal);
// Creates a Hook for the specified API function, in disabled state.
// Parameters:
// pszModule [in] A pointer to the loaded module name which contains the
// target function.
// pszTarget [in] A pointer to the target function name, which will be
// overridden by the detour function.
// pDetour [in] A pointer to the detour function, which will override
// the target function.
// ppOriginal [out] A pointer to the trampoline function, which will be
// used to call the original target function.
// This parameter can be NULL.
MH_STATUS WINAPI MH_CreateHookApi(
LPCWSTR pszModule, LPCSTR pszProcName, LPVOID pDetour, LPVOID *ppOriginal);
// Creates a Hook for the specified API function, in disabled state.
// Parameters:
// pszModule [in] A pointer to the loaded module name which contains the
// target function.
// pszTarget [in] A pointer to the target function name, which will be
// overridden by the detour function.
// pDetour [in] A pointer to the detour function, which will override
// the target function.
// ppOriginal [out] A pointer to the trampoline function, which will be
// used to call the original target function.
// This parameter can be NULL.
// ppTarget [out] A pointer to the target function, which will be used
// with other functions.
// This parameter can be NULL.
MH_STATUS WINAPI MH_CreateHookApiEx(
LPCWSTR pszModule, LPCSTR pszProcName, LPVOID pDetour, LPVOID *ppOriginal, LPVOID *ppTarget);
// Removes an already created hook.
// Parameters:
// pTarget [in] A pointer to the target function.
MH_STATUS WINAPI MH_RemoveHook(LPVOID pTarget);
// Enables an already created hook.
// Parameters:
// pTarget [in] A pointer to the target function.
// If this parameter is MH_ALL_HOOKS, all created hooks are
// enabled in one go.
MH_STATUS WINAPI MH_EnableHook(LPVOID pTarget);
// Disables an already created hook.
// Parameters:
// pTarget [in] A pointer to the target function.
// If this parameter is MH_ALL_HOOKS, all created hooks are
// disabled in one go.
MH_STATUS WINAPI MH_DisableHook(LPVOID pTarget);
// Queues to enable an already created hook.
// Parameters:
// pTarget [in] A pointer to the target function.
// If this parameter is MH_ALL_HOOKS, all created hooks are
// queued to be enabled.
MH_STATUS WINAPI MH_QueueEnableHook(LPVOID pTarget);
// Queues to disable an already created hook.
// Parameters:
// pTarget [in] A pointer to the target function.
// If this parameter is MH_ALL_HOOKS, all created hooks are
// queued to be disabled.
MH_STATUS WINAPI MH_QueueDisableHook(LPVOID pTarget);
// Applies all queued changes in one go.
MH_STATUS WINAPI MH_ApplyQueued(VOID);
// Translates the MH_STATUS to its name as a string.
const char * WINAPI MH_StatusToString(MH_STATUS status);
#ifdef __cplusplus
}
#endif
Binary file not shown.
+27
View File
@@ -0,0 +1,27 @@
///////////////////////////////////////////////////////////////////////////////
/// \file accumulators.hpp
/// Includes all of the Accumulators Framework
//
// Copyright 2005 Eric Niebler. Distributed under the Boost
// Software License, Version 1.0. (See accompanying file
// LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
#ifndef BOOST_ACCUMULATORS_ACCUMULATORS_HPP_EAN_28_10_2005
#define BOOST_ACCUMULATORS_ACCUMULATORS_HPP_EAN_28_10_2005
#include <boost/accumulators/framework/accumulator_set.hpp>
#include <boost/accumulators/framework/accumulator_concept.hpp>
#include <boost/accumulators/framework/accumulator_base.hpp>
#include <boost/accumulators/framework/extractor.hpp>
#include <boost/accumulators/framework/external.hpp>
#include <boost/accumulators/framework/features.hpp>
#include <boost/accumulators/framework/parameters/accumulator.hpp>
#include <boost/accumulators/framework/parameters/sample.hpp>
#include <boost/accumulators/framework/parameters/weight.hpp>
#include <boost/accumulators/framework/parameters/weights.hpp>
#include <boost/accumulators/framework/accumulators/external_accumulator.hpp>
#include <boost/accumulators/framework/accumulators/droppable_accumulator.hpp>
#include <boost/accumulators/framework/accumulators/reference_accumulator.hpp>
#include <boost/accumulators/framework/accumulators/value_accumulator.hpp>
#endif
+232
View File
@@ -0,0 +1,232 @@
///////////////////////////////////////////////////////////////////////////////
// accumulators_fwd.hpp
//
// Copyright 2005 Eric Niebler. Distributed under the Boost
// Software License, Version 1.0. (See accompanying file
// LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
#ifndef BOOST_ACCUMULATORS_ACCUMULATORS_FWD_HPP_EAN_28_10_2005
#define BOOST_ACCUMULATORS_ACCUMULATORS_FWD_HPP_EAN_28_10_2005
#include <boost/config.hpp>
#include <boost/core/enable_if.hpp>
#include <boost/parameter/is_argument_pack.hpp>
#include <boost/mpl/apply_fwd.hpp> // for mpl::na
#include <boost/mpl/limits/vector.hpp>
#include <boost/preprocessor/cat.hpp>
#include <boost/preprocessor/arithmetic/inc.hpp>
#include <boost/preprocessor/repetition/enum_params_with_a_default.hpp>
#include <boost/preprocessor/repetition/enum_trailing_params.hpp>
#include <boost/preprocessor/repetition/enum_trailing_binary_params.hpp>
#include <boost/preprocessor/repetition/repeat_from_to.hpp>
#include <boost/accumulators/numeric/functional_fwd.hpp>
#ifndef BOOST_ACCUMULATORS_MAX_FEATURES
/// The maximum number of accumulators that may be put in an accumulator_set.
/// Defaults to BOOST_MPL_LIMIT_VECTOR_SIZE (which defaults to 20).
# define BOOST_ACCUMULATORS_MAX_FEATURES BOOST_MPL_LIMIT_VECTOR_SIZE
#endif
#if BOOST_ACCUMULATORS_MAX_FEATURES > BOOST_MPL_LIMIT_VECTOR_SIZE
# error BOOST_ACCUMULATORS_MAX_FEATURES cannot be larger than BOOST_MPL_LIMIT_VECTOR_SIZE
#endif
#ifndef BOOST_ACCUMULATORS_MAX_ARGS
/// The maximum number of arguments that may be specified to an accumulator_set's
/// accumulation function. Defaults to 15.
# define BOOST_ACCUMULATORS_MAX_ARGS 15
#endif
#if BOOST_WORKAROUND(__GNUC__, == 3) \
|| BOOST_WORKAROUND(__EDG_VERSION__, BOOST_TESTED_AT(306))
# define BOOST_ACCUMULATORS_BROKEN_CONST_OVERLOADS
#endif
#ifdef BOOST_ACCUMULATORS_BROKEN_CONST_OVERLOADS
# include <boost/type_traits/is_const.hpp>
# define BOOST_ACCUMULATORS_PROTO_DISABLE_IF_IS_CONST(T)\
, typename boost::disable_if<boost::is_const<T> >::type * = 0
#else
# define BOOST_ACCUMULATORS_PROTO_DISABLE_IF_IS_CONST(T)
#endif
#define BOOST_ACCUMULATORS_GCC_VERSION \
(__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__)
namespace boost { namespace accumulators
{
///////////////////////////////////////////////////////////////////////////////
// Named parameters tags
//
namespace tag
{
struct sample;
struct weight;
struct accumulator;
struct weights;
}
///////////////////////////////////////////////////////////////////////////////
// User-level features
//
namespace tag
{
template<typename ValueType, typename Tag>
struct value;
template<typename Tag>
struct value_tag;
template<typename Referent, typename Tag>
struct reference;
template<typename Tag>
struct reference_tag;
template<typename Type, typename Tag = void, typename AccumulatorSet = void>
struct external;
template<typename Feature>
struct droppable;
}
template<typename Accumulator>
struct droppable_accumulator_base;
template<typename Accumulator>
struct droppable_accumulator;
template<typename Accumulator>
struct with_cached_result;
template<typename Sample, typename Features, typename Weight = void>
struct accumulator_set;
template<typename Feature>
struct extractor;
template<typename Feature>
struct feature_of;
template<typename Feature>
struct as_feature;
template<typename Feature>
struct as_weighted_feature;
template<BOOST_PP_ENUM_PARAMS_WITH_A_DEFAULT(BOOST_ACCUMULATORS_MAX_FEATURES, typename Feature, mpl::na)>
struct depends_on;
template<BOOST_PP_ENUM_PARAMS_WITH_A_DEFAULT(BOOST_ACCUMULATORS_MAX_FEATURES, typename Feature, mpl::na)>
struct features;
template<typename Feature, typename AccumulatorSet>
typename mpl::apply<AccumulatorSet, Feature>::type const &
find_accumulator(AccumulatorSet const &acc);
template<typename Feature, typename AccumulatorSet>
typename mpl::apply<AccumulatorSet, Feature>::type::result_type
extract_result(AccumulatorSet const &acc);
namespace detail
{
struct _enabler
{
};
}
// ... other overloads generated by Boost.Preprocessor:
/// INTERNAL ONLY
///
#define BOOST_ACCUMULATORS_EXTRACT_RESULT_FWD(z, n, _) \
template< \
typename Feature \
, typename AccumulatorSet \
BOOST_PP_ENUM_TRAILING_PARAMS_Z(z, n, typename A) \
> \
typename mpl::apply<AccumulatorSet, Feature>::type::result_type \
extract_result( \
AccumulatorSet const &acc \
BOOST_PP_ENUM_TRAILING_BINARY_PARAMS_Z(z, n, A, const &a) \
, typename boost::enable_if< \
parameter::is_argument_pack<A0> \
, detail::_enabler \
>::type = detail::_enabler() \
); \
template< \
typename Feature \
, typename AccumulatorSet \
BOOST_PP_ENUM_TRAILING_PARAMS_Z(z, n, typename A) \
> \
typename mpl::apply<AccumulatorSet, Feature>::type::result_type \
extract_result( \
AccumulatorSet const &acc \
BOOST_PP_ENUM_TRAILING_BINARY_PARAMS_Z(z, n, A, const &a) \
, typename boost::disable_if< \
parameter::is_argument_pack<A0> \
, detail::_enabler \
>::type = detail::_enabler() \
);
/// INTERNAL ONLY
///
BOOST_PP_REPEAT_FROM_TO(
1
, BOOST_PP_INC(BOOST_ACCUMULATORS_MAX_ARGS)
, BOOST_ACCUMULATORS_EXTRACT_RESULT_FWD
, _
)
#undef BOOST_ACCUMULATORS_EXTRACT_RESULT_FWD
#ifdef BOOST_ACCUMULATORS_DOXYGEN_INVOKED
template<typename Feature, typename AccumulatorSet, typename A1, typename A2 ...>
typename mpl::apply<AccumulatorSet, Feature>::type::result_type
extract_result(AccumulatorSet const &acc, A1 const &a1, A2 const &a2 ...);
#endif
namespace impl
{
using namespace numeric::operators;
template<typename Accumulator, typename Tag>
struct external_impl;
}
namespace detail
{
template<typename Accumulator>
struct feature_tag;
template<typename Feature, typename Sample, typename Weight>
struct to_accumulator;
struct accumulator_set_base;
template<typename T>
struct is_accumulator_set;
inline void ignore_variable(void const *) {}
}
}} // namespace boost::accumulators
#define BOOST_ACCUMULATORS_IGNORE_GLOBAL(X) \
namespace detail \
{ \
struct BOOST_PP_CAT(ignore_, X) \
{ \
void ignore() \
{ \
boost::accumulators::detail::ignore_variable(&X); \
} \
}; \
} \
/**/
#include <boost/parameter/nested_keyword.hpp>
#endif // include guard
@@ -0,0 +1,65 @@
///////////////////////////////////////////////////////////////////////////////
// accumulator_base.hpp
//
// Copyright 2005 Eric Niebler. Distributed under the Boost
// Software License, Version 1.0. (See accompanying file
// LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
#ifndef BOOST_ACCUMULATORS_FRAMEWORK_ACCUMULATORS_BASE_HPP_EAN_28_10_2005
#define BOOST_ACCUMULATORS_FRAMEWORK_ACCUMULATORS_BASE_HPP_EAN_28_10_2005
#include <boost/mpl/placeholders.hpp>
#include <boost/mpl/joint_view.hpp>
#include <boost/mpl/single_view.hpp>
#include <boost/mpl/fold.hpp>
#include <boost/mpl/contains.hpp>
#include <boost/mpl/empty_sequence.hpp>
#include <boost/accumulators/framework/accumulator_concept.hpp>
namespace boost { namespace accumulators
{
namespace detail
{
typedef void void_;
}
///////////////////////////////////////////////////////////////////////////////
// dont_care
//
struct dont_care
{
template<typename Args>
dont_care(Args const &)
{
}
};
///////////////////////////////////////////////////////////////////////////////
// accumulator_base
//
struct accumulator_base
{
// hidden if defined in derived classes
detail::void_ operator ()(dont_care)
{
}
typedef mpl::false_ is_droppable;
detail::void_ add_ref(dont_care)
{
}
detail::void_ drop(dont_care)
{
}
detail::void_ on_drop(dont_care)
{
}
};
}} // namespace boost::accumulators
#endif
@@ -0,0 +1,29 @@
///////////////////////////////////////////////////////////////////////////////
// accumulator_concept.hpp
//
// Copyright 2005 Eric Niebler. Distributed under the Boost
// Software License, Version 1.0. (See accompanying file
// LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
#ifndef BOOST_ACCUMULATORS_FRAMEWORK_ACCUMULATOR_CONCEPT_HPP_EAN_28_10_2005
#define BOOST_ACCUMULATORS_FRAMEWORK_ACCUMULATOR_CONCEPT_HPP_EAN_28_10_2005
#include <boost/concept_check.hpp>
namespace boost { namespace accumulators
{
template<typename Stat>
struct accumulator_concept
{
void constraints()
{
// TODO: define the stat concept
}
Stat stat;
};
}} // namespace boost::accumulators
#endif
@@ -0,0 +1,549 @@
///////////////////////////////////////////////////////////////////////////////
// accumulator_set.hpp
//
// Copyright 2005 Eric Niebler. Distributed under the Boost
// Software License, Version 1.0. (See accompanying file
// LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
#ifndef BOOST_ACCUMULATORS_FRAMEWORK_ACCUMULATOR_SET_HPP_EAN_28_10_2005
#define BOOST_ACCUMULATORS_FRAMEWORK_ACCUMULATOR_SET_HPP_EAN_28_10_2005
#include <boost/version.hpp>
#include <boost/mpl/bool.hpp>
#include <boost/mpl/if.hpp>
#include <boost/mpl/apply.hpp>
#include <boost/mpl/assert.hpp>
#include <boost/mpl/protect.hpp>
#include <boost/mpl/identity.hpp>
#include <boost/mpl/is_sequence.hpp>
#include <boost/type_traits/is_same.hpp>
#include <boost/type_traits/is_base_of.hpp>
#include <boost/type_traits/remove_const.hpp>
#include <boost/type_traits/remove_reference.hpp>
#include <boost/core/enable_if.hpp>
#include <boost/parameter/is_argument_pack.hpp>
#include <boost/preprocessor/repetition/repeat_from_to.hpp>
#include <boost/preprocessor/repetition/enum_params.hpp>
#include <boost/preprocessor/repetition/enum_binary_params.hpp>
#include <boost/preprocessor/repetition/enum_trailing_params.hpp>
#include <boost/accumulators/accumulators_fwd.hpp>
#include <boost/accumulators/framework/depends_on.hpp>
#include <boost/accumulators/framework/accumulator_concept.hpp>
#include <boost/accumulators/framework/parameters/accumulator.hpp>
#include <boost/accumulators/framework/parameters/sample.hpp>
#include <boost/accumulators/framework/accumulators/external_accumulator.hpp>
#include <boost/accumulators/framework/accumulators/droppable_accumulator.hpp>
#include <boost/fusion/include/any.hpp>
#include <boost/fusion/include/find_if.hpp>
#include <boost/fusion/include/for_each.hpp>
#include <boost/fusion/include/filter_view.hpp>
namespace boost { namespace accumulators
{
namespace detail
{
///////////////////////////////////////////////////////////////////////////////
// accumulator_visitor
// wrap a boost::parameter argument pack in a Fusion extractor object
template<typename Args>
struct accumulator_visitor
{
explicit accumulator_visitor(Args const &a)
: args(a)
{
}
accumulator_visitor(accumulator_visitor const &other)
: args(other.args)
{
}
template<typename Accumulator>
void operator ()(Accumulator &accumulator) const
{
accumulator(this->args);
}
private:
BOOST_DELETED_FUNCTION(accumulator_visitor &operator =(accumulator_visitor const &))
Args const &args;
};
template<typename Args>
inline accumulator_visitor<Args> const make_accumulator_visitor(Args const &args)
{
return accumulator_visitor<Args>(args);
}
///////////////////////////////////////////////////////////////////////////////
// accumulator_set_base
struct accumulator_set_base
{
};
///////////////////////////////////////////////////////////////////////////////
// is_accumulator_set
template<typename T>
struct is_accumulator_set
: mpl::if_<
boost::is_base_of<
accumulator_set_base
, typename boost::remove_const<
typename boost::remove_reference<T>::type
>::type
>
, mpl::true_
, mpl::false_
>::type
{
};
// function object that serialize an accumulator
template<typename Archive>
struct serialize_accumulator
{
serialize_accumulator(Archive & _ar, const unsigned int _file_version) :
ar(_ar), file_version(_file_version)
{}
template<typename Accumulator>
void operator ()(Accumulator &accumulator)
{
accumulator.serialize(ar, file_version);
}
private:
Archive& ar;
const unsigned int file_version;
};
} // namespace detail
#ifdef _MSC_VER
#pragma warning(push)
#pragma warning(disable: 4355) // warning C4355: 'this' : used in base member initializer list
#endif
///////////////////////////////////////////////////////////////////////////////
/// \brief A set of accumulators.
///
/// accumulator_set resolves the dependencies between features and ensures that
/// the accumulators in the set are updated in the proper order.
///
/// acccumulator_set provides a general mechanism to visit the accumulators
/// in the set in order, with or without a filter. You can also fetch a reference
/// to an accumulator that corresponds to a feature.
///
template<typename Sample, typename Features, typename Weight>
struct accumulator_set
: detail::accumulator_set_base
{
typedef Sample sample_type; ///< The type of the samples that will be accumulated
typedef Features features_type; ///< An MPL sequence of the features that should be accumulated.
typedef Weight weight_type; ///< The type of the weight parameter. Must be a scalar. Defaults to void.
/// INTERNAL ONLY
///
typedef
typename detail::make_accumulator_tuple<
Features
, Sample
, Weight
>::type
accumulators_mpl_vector;
// generate a fusion::list of accumulators
/// INTERNAL ONLY
///
typedef
typename detail::meta::make_acc_list<
accumulators_mpl_vector
>::type
accumulators_type;
/// INTERNAL ONLY
///
//BOOST_MPL_ASSERT((mpl::is_sequence<accumulators_type>));
///////////////////////////////////////////////////////////////////////////////
/// default-construct all contained accumulators
accumulator_set()
: accumulators(
detail::make_acc_list(
accumulators_mpl_vector()
, (boost::accumulators::accumulator = *this)
)
)
{
// Add-ref the Features that the user has specified
this->template visit_if<detail::contains_feature_of_<Features> >(
detail::make_add_ref_visitor(boost::accumulators::accumulator = *this)
);
}
/// \overload
///
/// \param a1 Optional named parameter to be passed to all the accumulators
template<typename A1>
explicit accumulator_set(
A1 const &a1
, typename boost::enable_if<
parameter::is_argument_pack<A1>
, detail::_enabler
>::type = detail::_enabler()
) : accumulators(
detail::make_acc_list(
accumulators_mpl_vector()
, (boost::accumulators::accumulator = *this, a1)
)
)
{
// Add-ref the Features that the user has specified
this->template visit_if<detail::contains_feature_of_<Features> >(
detail::make_add_ref_visitor(boost::accumulators::accumulator = *this)
);
}
/// \overload
///
/// \param a1 Optional sample parameter to be passed to all the accumulators
template<typename A1>
explicit accumulator_set(
A1 const &a1
, typename boost::disable_if<
parameter::is_argument_pack<A1>
, detail::_enabler
>::type = detail::_enabler()
) : accumulators(
detail::make_acc_list(
accumulators_mpl_vector()
, (
boost::accumulators::accumulator = *this
, boost::accumulators::sample = a1
)
)
)
{
// Add-ref the Features that the user has specified
this->template visit_if<detail::contains_feature_of_<Features> >(
detail::make_add_ref_visitor(boost::accumulators::accumulator = *this)
);
}
// ... other overloads generated by Boost.Preprocessor:
/// INTERNAL ONLY
///
#define BOOST_ACCUMULATORS_ACCUMULATOR_SET_CTOR(z, n, _) \
template<BOOST_PP_ENUM_PARAMS_Z(z, n, typename A)> \
accumulator_set( \
BOOST_PP_ENUM_BINARY_PARAMS_Z(z, n, A, const &a) \
, typename boost::enable_if< \
parameter::is_argument_pack<A0> \
, detail::_enabler \
>::type = detail::_enabler() \
) : accumulators( \
detail::make_acc_list( \
accumulators_mpl_vector() \
, ( \
boost::accumulators::accumulator = *this \
BOOST_PP_ENUM_TRAILING_PARAMS_Z(z, n, a) \
) \
) \
) \
{ \
/* Add-ref the Features that the user has specified */ \
this->template visit_if<detail::contains_feature_of_<Features> >( \
detail::make_add_ref_visitor(boost::accumulators::accumulator = *this) \
); \
} \
template<BOOST_PP_ENUM_PARAMS_Z(z, n, typename A)> \
accumulator_set( \
BOOST_PP_ENUM_BINARY_PARAMS_Z(z, n, A, const &a) \
, typename boost::disable_if< \
parameter::is_argument_pack<A0> \
, detail::_enabler \
>::type = detail::_enabler() \
) : accumulators( \
detail::make_acc_list( \
accumulators_mpl_vector() \
, ( \
boost::accumulators::accumulator = *this \
, boost::accumulators::sample = BOOST_PP_ENUM_PARAMS_Z(z, n, a) \
) \
) \
) \
{ \
/* Add-ref the Features that the user has specified */ \
this->template visit_if<detail::contains_feature_of_<Features> >( \
detail::make_add_ref_visitor(boost::accumulators::accumulator = *this) \
); \
}
/// INTERNAL ONLY
///
BOOST_PP_REPEAT_FROM_TO(
2
, BOOST_PP_INC(BOOST_ACCUMULATORS_MAX_ARGS)
, BOOST_ACCUMULATORS_ACCUMULATOR_SET_CTOR
, _
)
#ifdef BOOST_ACCUMULATORS_DOXYGEN_INVOKED
/// \overload
///
template<typename A1, typename A2, ...>
accumulator_set(A1 const &a1, A2 const &a2, ...);
#endif
// ... other overloads generated by Boost.Preprocessor below ...
///////////////////////////////////////////////////////////////////////////////
/// Visitation
/// \param func UnaryFunction which is invoked with each accumulator in turn.
template<typename UnaryFunction>
void visit(UnaryFunction const &func)
{
fusion::for_each(this->accumulators, func);
}
///////////////////////////////////////////////////////////////////////////////
/// Conditional visitation
/// \param func UnaryFunction which is invoked with each accumulator in turn,
/// provided the accumulator satisfies the MPL predicate FilterPred.
template<typename FilterPred, typename UnaryFunction>
void visit_if(UnaryFunction const &func)
{
fusion::filter_view<accumulators_type, FilterPred> filtered_accs(this->accumulators);
fusion::for_each(filtered_accs, func);
}
///////////////////////////////////////////////////////////////////////////////
/// The return type of the operator() overloads is void.
typedef void result_type;
///////////////////////////////////////////////////////////////////////////////
/// Accumulation
/// \param a1 Optional named parameter to be passed to all the accumulators
void operator ()()
{
this->visit(
detail::make_accumulator_visitor(
boost::accumulators::accumulator = *this
)
);
}
// ... other overloads generated by Boost.Preprocessor:
/// INTERNAL ONLY
///
#define BOOST_ACCUMULATORS_ACCUMULATOR_SET_FUN_OP(z, n, _) \
template<BOOST_PP_ENUM_PARAMS_Z(z, n, typename A)> \
void operator ()( \
BOOST_PP_ENUM_BINARY_PARAMS_Z(z, n, A, const &a) \
, typename boost::enable_if< \
parameter::is_argument_pack<A0> \
, detail::_enabler \
>::type = detail::_enabler() \
) \
{ \
this->visit( \
detail::make_accumulator_visitor( \
( \
boost::accumulators::accumulator = *this \
BOOST_PP_ENUM_TRAILING_PARAMS_Z(z, n, a) \
) \
) \
); \
} \
template<BOOST_PP_ENUM_PARAMS_Z(z, n, typename A)> \
void operator ()( \
BOOST_PP_ENUM_BINARY_PARAMS_Z(z, n, A, const &a) \
, typename boost::disable_if< \
parameter::is_argument_pack<A0> \
, detail::_enabler \
>::type = detail::_enabler() \
) \
{ \
this->visit( \
detail::make_accumulator_visitor( \
( \
boost::accumulators::accumulator = *this \
, boost::accumulators::sample = BOOST_PP_ENUM_PARAMS_Z(z, n, a) \
) \
) \
); \
}
/// INTERNAL ONLY
///
BOOST_PP_REPEAT_FROM_TO(
1
, BOOST_PP_INC(BOOST_ACCUMULATORS_MAX_ARGS)
, BOOST_ACCUMULATORS_ACCUMULATOR_SET_FUN_OP
, _
)
#ifdef BOOST_ACCUMULATORS_DOXYGEN_INVOKED
/// \overload
///
template<typename A1, typename A2, ...>
void operator ()(A1 const &a1, A2 const &a2, ...);
#endif
///////////////////////////////////////////////////////////////////////////////
/// Extraction
template<typename Feature>
struct apply
: fusion::result_of::value_of<
typename fusion::result_of::find_if<
accumulators_type
, detail::matches_feature<Feature>
>::type
>
{
};
///////////////////////////////////////////////////////////////////////////////
/// Extraction
template<typename Feature>
typename apply<Feature>::type &extract()
{
return *fusion::find_if<detail::matches_feature<Feature> >(this->accumulators);
}
/// \overload
template<typename Feature>
typename apply<Feature>::type const &extract() const
{
return *fusion::find_if<detail::matches_feature<Feature> >(this->accumulators);
}
///////////////////////////////////////////////////////////////////////////////
/// Drop
template<typename Feature>
void drop()
{
// You can only drop the features that you have specified explicitly
typedef typename apply<Feature>::type the_accumulator;
BOOST_MPL_ASSERT((detail::contains_feature_of<Features, the_accumulator>));
typedef
typename feature_of<typename as_feature<Feature>::type>::type
the_feature;
(*fusion::find_if<detail::matches_feature<Feature> >(this->accumulators))
.drop(boost::accumulators::accumulator = *this);
// Also drop accumulators that this feature depends on
typedef typename the_feature::dependencies dependencies;
this->template visit_if<detail::contains_feature_of_<dependencies> >(
detail::make_drop_visitor(boost::accumulators::accumulator = *this)
);
}
// make the accumulator set serializeable
template<class Archive>
void serialize(Archive & ar, const unsigned int file_version)
{
detail::serialize_accumulator<Archive> serializer(ar, file_version);
fusion::for_each(this->accumulators, serializer);
}
private:
accumulators_type accumulators;
};
#ifdef _MSC_VER
#pragma warning(pop)
#endif
///////////////////////////////////////////////////////////////////////////////
// find_accumulator
// find an accumulator in an accumulator_set corresponding to a feature
template<typename Feature, typename AccumulatorSet>
typename mpl::apply<AccumulatorSet, Feature>::type &
find_accumulator(AccumulatorSet &acc BOOST_ACCUMULATORS_PROTO_DISABLE_IF_IS_CONST(AccumulatorSet))
{
return acc.template extract<Feature>();
}
/// \overload
template<typename Feature, typename AccumulatorSet>
typename mpl::apply<AccumulatorSet, Feature>::type const &
find_accumulator(AccumulatorSet const &acc)
{
return acc.template extract<Feature>();
}
template<typename Feature, typename AccumulatorSet>
typename mpl::apply<AccumulatorSet, Feature>::type::result_type
extract_result(AccumulatorSet const &acc)
{
return find_accumulator<Feature>(acc).result(
boost::accumulators::accumulator = acc
);
}
///////////////////////////////////////////////////////////////////////////////
// extract_result
// extract a result from an accumulator set
/// INTERNAL ONLY
///
#define BOOST_ACCUMULATORS_EXTRACT_RESULT_FUN(z, n, _) \
template< \
typename Feature \
, typename AccumulatorSet \
BOOST_PP_ENUM_TRAILING_PARAMS_Z(z, n, typename A) \
> \
typename mpl::apply<AccumulatorSet, Feature>::type::result_type \
extract_result( \
AccumulatorSet const &acc \
BOOST_PP_ENUM_TRAILING_BINARY_PARAMS_Z(z, n, A, const &a) \
, typename boost::enable_if< \
parameter::is_argument_pack<A0> \
, detail::_enabler \
>::type \
) \
{ \
return find_accumulator<Feature>(acc).result( \
( \
boost::accumulators::accumulator = acc \
BOOST_PP_ENUM_TRAILING_PARAMS_Z(z, n, a) \
) \
); \
} \
template< \
typename Feature \
, typename AccumulatorSet \
BOOST_PP_ENUM_TRAILING_PARAMS_Z(z, n, typename A) \
> \
typename mpl::apply<AccumulatorSet, Feature>::type::result_type \
extract_result( \
AccumulatorSet const &acc \
BOOST_PP_ENUM_TRAILING_BINARY_PARAMS_Z(z, n, A, const &a) \
, typename boost::disable_if< \
parameter::is_argument_pack<A0> \
, detail::_enabler \
>::type \
) \
{ \
return find_accumulator<Feature>(acc).result(( \
boost::accumulators::accumulator = acc \
, boost::accumulators::sample = BOOST_PP_ENUM_PARAMS_Z(z, n, a) \
)); \
}
BOOST_PP_REPEAT_FROM_TO(
1
, BOOST_PP_INC(BOOST_ACCUMULATORS_MAX_ARGS)
, BOOST_ACCUMULATORS_EXTRACT_RESULT_FUN
, _
)
}} // namespace boost::accumulators
#endif
@@ -0,0 +1,333 @@
///////////////////////////////////////////////////////////////////////////////
// droppable_accumulator.hpp
//
// Copyright 2005 Eric Niebler. Distributed under the Boost
// Software License, Version 1.0. (See accompanying file
// LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
#ifndef BOOST_ACCUMULATORS_FRAMEWORK_ACCUMULATORS_DROPPABLE_ACCUMULATOR_HPP_EAN_13_12_2005
#define BOOST_ACCUMULATORS_FRAMEWORK_ACCUMULATORS_DROPPABLE_ACCUMULATOR_HPP_EAN_13_12_2005
#include <new>
#include <boost/assert.hpp>
#include <boost/mpl/apply.hpp>
#include <boost/aligned_storage.hpp>
#include <boost/accumulators/framework/depends_on.hpp> // for feature_of
#include <boost/accumulators/framework/parameters/accumulator.hpp> // for accumulator
namespace boost { namespace accumulators
{
template<typename Accumulator>
struct droppable_accumulator;
namespace detail
{
///////////////////////////////////////////////////////////////////////////////
// add_ref_visitor
// a fusion function object for add_ref'ing accumulators
template<typename Args>
struct add_ref_visitor
{
explicit add_ref_visitor(Args const &args)
: args_(args)
{
}
add_ref_visitor(add_ref_visitor const &other)
: args_(other.args_)
{
}
template<typename Accumulator>
void operator ()(Accumulator &acc) const
{
typedef typename Accumulator::feature_tag::dependencies dependencies;
acc.add_ref(this->args_);
// Also add_ref accumulators that this feature depends on
this->args_[accumulator].template
visit_if<detail::contains_feature_of_<dependencies> >(
*this
);
}
private:
BOOST_DELETED_FUNCTION(add_ref_visitor &operator =(add_ref_visitor const &))
Args const &args_;
};
template<typename Args>
add_ref_visitor<Args> make_add_ref_visitor(Args const &args)
{
return add_ref_visitor<Args>(args);
}
///////////////////////////////////////////////////////////////////////////////
// drop_visitor
// a fusion function object for dropping accumulators
template<typename Args>
struct drop_visitor
{
explicit drop_visitor(Args const &args)
: args_(args)
{
}
template<typename Accumulator>
void operator ()(Accumulator &acc) const
{
if(typename Accumulator::is_droppable())
{
typedef typename Accumulator::feature_tag::dependencies dependencies;
acc.drop(this->args_);
// Also drop accumulators that this feature depends on
this->args_[accumulator].template
visit_if<detail::contains_feature_of_<dependencies> >(
*this
);
}
}
private:
BOOST_DELETED_FUNCTION(drop_visitor &operator =(drop_visitor const &))
Args const &args_;
};
template<typename Args>
drop_visitor<Args> make_drop_visitor(Args const &args)
{
return drop_visitor<Args>(args);
}
}
//////////////////////////////////////////////////////////////////////////
// droppable_accumulator_base
template<typename Accumulator>
struct droppable_accumulator_base
: Accumulator
{
typedef droppable_accumulator_base base;
typedef mpl::true_ is_droppable;
typedef typename Accumulator::result_type result_type;
template<typename Args>
droppable_accumulator_base(Args const &args)
: Accumulator(args)
, ref_count_(0)
{
}
droppable_accumulator_base(droppable_accumulator_base const &that)
: Accumulator(*static_cast<Accumulator const *>(&that))
, ref_count_(that.ref_count_)
{
}
template<typename Args>
void operator ()(Args const &args)
{
if(!this->is_dropped())
{
this->Accumulator::operator ()(args);
}
}
template<typename Args>
void add_ref(Args const &)
{
++this->ref_count_;
}
template<typename Args>
void drop(Args const &args)
{
BOOST_ASSERT(0 < this->ref_count_);
if(1 == this->ref_count_)
{
static_cast<droppable_accumulator<Accumulator> *>(this)->on_drop(args);
}
--this->ref_count_;
}
bool is_dropped() const
{
return 0 == this->ref_count_;
}
private:
int ref_count_;
};
//////////////////////////////////////////////////////////////////////////
// droppable_accumulator
// this can be specialized for any type that needs special handling
template<typename Accumulator>
struct droppable_accumulator
: droppable_accumulator_base<Accumulator>
{
template<typename Args>
droppable_accumulator(Args const &args)
: droppable_accumulator::base(args)
{
}
droppable_accumulator(droppable_accumulator const &that)
: droppable_accumulator::base(*static_cast<typename droppable_accumulator::base const *>(&that))
{
}
};
//////////////////////////////////////////////////////////////////////////
// with_cached_result
template<typename Accumulator>
struct with_cached_result
: Accumulator
{
typedef typename Accumulator::result_type result_type;
template<typename Args>
with_cached_result(Args const &args)
: Accumulator(args)
, cache()
{
}
with_cached_result(with_cached_result const &that)
: Accumulator(*static_cast<Accumulator const *>(&that))
, cache()
{
if(that.has_result())
{
this->set(that.get());
}
}
~with_cached_result()
{
// Since this is a base class of droppable_accumulator_base,
// this destructor is called before any of droppable_accumulator_base's
// members get cleaned up, including is_dropped, so the following
// call to has_result() is valid.
if(this->has_result())
{
this->get().~result_type();
}
}
template<typename Args>
void on_drop(Args const &args)
{
// cache the result at the point this calculation was dropped
BOOST_ASSERT(!this->has_result());
this->set(this->Accumulator::result(args));
}
template<typename Args>
result_type result(Args const &args) const
{
return this->has_result() ? this->get() : this->Accumulator::result(args);
}
private:
BOOST_DELETED_FUNCTION(with_cached_result &operator =(with_cached_result const &))
void set(result_type const &r)
{
::new(this->cache.address()) result_type(r);
}
result_type const &get() const
{
return *static_cast<result_type const *>(this->cache.address());
}
bool has_result() const
{
typedef with_cached_result<Accumulator> this_type;
typedef droppable_accumulator_base<this_type> derived_type;
return static_cast<derived_type const *>(this)->is_dropped();
}
aligned_storage<sizeof(result_type)> cache;
};
namespace tag
{
template<typename Feature>
struct as_droppable
{
typedef droppable<Feature> type;
};
template<typename Feature>
struct as_droppable<droppable<Feature> >
{
typedef droppable<Feature> type;
};
//////////////////////////////////////////////////////////////////////////
// droppable
template<typename Feature>
struct droppable
: as_feature<Feature>::type
{
typedef typename as_feature<Feature>::type feature_type;
typedef typename feature_type::dependencies tmp_dependencies_;
typedef
typename mpl::transform<
typename feature_type::dependencies
, as_droppable<mpl::_1>
>::type
dependencies;
struct impl
{
template<typename Sample, typename Weight>
struct apply
{
typedef
droppable_accumulator<
typename mpl::apply2<typename feature_type::impl, Sample, Weight>::type
>
type;
};
};
};
}
// make droppable<tag::feature(modifier)> work
template<typename Feature>
struct as_feature<tag::droppable<Feature> >
{
typedef tag::droppable<typename as_feature<Feature>::type> type;
};
// make droppable<tag::mean> work with non-void weights (should become
// droppable<tag::weighted_mean>
template<typename Feature>
struct as_weighted_feature<tag::droppable<Feature> >
{
typedef tag::droppable<typename as_weighted_feature<Feature>::type> type;
};
// for the purposes of feature-based dependency resolution,
// droppable<Foo> provides the same feature as Foo
template<typename Feature>
struct feature_of<tag::droppable<Feature> >
: feature_of<Feature>
{
};
// Note: Usually, the extractor is pulled into the accumulators namespace with
// a using directive, not the tag. But the droppable<> feature doesn't have an
// extractor, so we can put the droppable tag in the accumulators namespace
// without fear of a name conflict.
using tag::droppable;
}} // namespace boost::accumulators
#endif
@@ -0,0 +1,108 @@
///////////////////////////////////////////////////////////////////////////////
// external_accumulator.hpp
//
// Copyright 2005 Eric Niebler. Distributed under the Boost
// Software License, Version 1.0. (See accompanying file
// LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
#ifndef BOOST_ACCUMULATORS_FRAMEWORK_ACCUMULATORS_EXTERNAL_ACCUMULATOR_HPP_EAN_01_12_2005
#define BOOST_ACCUMULATORS_FRAMEWORK_ACCUMULATORS_EXTERNAL_ACCUMULATOR_HPP_EAN_01_12_2005
#include <boost/mpl/placeholders.hpp>
#include <boost/parameter/keyword.hpp>
#include <boost/accumulators/framework/extractor.hpp>
#include <boost/accumulators/framework/depends_on.hpp> // for feature_tag
#include <boost/accumulators/framework/accumulator_base.hpp>
#include <boost/accumulators/framework/accumulators/reference_accumulator.hpp>
namespace boost { namespace accumulators { namespace impl
{
//////////////////////////////////////////////////////////////////////////
// external_impl
/// INTERNAL ONLY
///
template<typename Accumulator, typename Tag>
struct external_impl
: accumulator_base
{
typedef typename Accumulator::result_type result_type;
typedef typename detail::feature_tag<Accumulator>::type feature_tag;
external_impl(dont_care) {}
template<typename Args>
result_type result(Args const &args) const
{
return this->extract_(args, args[parameter::keyword<Tag>::instance | 0]);
}
private:
template<typename Args>
static result_type extract_(Args const &args, int)
{
// No named parameter passed to the extractor. Maybe the external
// feature is held by reference<>.
extractor<feature_tag> extract;
return extract(accumulators::reference_tag<Tag>(args));
}
template<typename Args, typename AccumulatorSet>
static result_type extract_(Args const &, AccumulatorSet const &acc)
{
// OK, a named parameter for this external feature was passed to the
// extractor, so use that.
extractor<feature_tag> extract;
return extract(acc);
}
};
} // namespace impl
namespace tag
{
//////////////////////////////////////////////////////////////////////////
// external
template<typename Feature, typename Tag, typename AccumulatorSet>
struct external
: depends_on<reference<AccumulatorSet, Tag> >
{
typedef
accumulators::impl::external_impl<
detail::to_accumulator<Feature, mpl::_1, mpl::_2>
, Tag
>
impl;
};
template<typename Feature, typename Tag>
struct external<Feature, Tag, void>
: depends_on<>
{
typedef
accumulators::impl::external_impl<
detail::to_accumulator<Feature, mpl::_1, mpl::_2>
, Tag
>
impl;
};
}
// for the purposes of feature-based dependency resolution,
// external_accumulator<Feature, Tag> provides the same feature as Feature
template<typename Feature, typename Tag, typename AccumulatorSet>
struct feature_of<tag::external<Feature, Tag, AccumulatorSet> >
: feature_of<Feature>
{
};
// Note: Usually, the extractor is pulled into the accumulators namespace with
// a using directive, not the tag. But the external<> feature doesn't have an
// extractor, so we can put the external tag in the accumulators namespace
// without fear of a name conflict.
using tag::external;
}} // namespace boost::accumulators
#endif
@@ -0,0 +1,89 @@
///////////////////////////////////////////////////////////////////////////////
// reference_accumulator.hpp
//
// Copyright 2005 Eric Niebler. Distributed under the Boost
// Software License, Version 1.0. (See accompanying file
// LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
#ifndef BOOST_ACCUMULATORS_FRAMEWORK_ACCUMULATORS_REFERENCE_ACCUMULATOR_HPP_EAN_03_23_2006
#define BOOST_ACCUMULATORS_FRAMEWORK_ACCUMULATORS_REFERENCE_ACCUMULATOR_HPP_EAN_03_23_2006
#include <boost/ref.hpp>
#include <boost/mpl/always.hpp>
#include <boost/parameter/keyword.hpp>
#include <boost/accumulators/framework/depends_on.hpp> // for feature_tag
#include <boost/accumulators/framework/accumulator_base.hpp>
#include <boost/accumulators/framework/extractor.hpp>
namespace boost { namespace accumulators
{
namespace impl
{
//////////////////////////////////////////////////////////////////////////
// reference_accumulator_impl
//
template<typename Referent, typename Tag>
struct reference_accumulator_impl
: accumulator_base
{
typedef Referent &result_type;
template<typename Args>
reference_accumulator_impl(Args const &args)
: ref(args[parameter::keyword<Tag>::instance])
{
}
result_type result(dont_care) const
{
return this->ref;
}
private:
reference_wrapper<Referent> ref;
};
} // namespace impl
namespace tag
{
//////////////////////////////////////////////////////////////////////////
// reference_tag
template<typename Tag>
struct reference_tag
{
};
//////////////////////////////////////////////////////////////////////////
// reference
template<typename Referent, typename Tag>
struct reference
: depends_on<>
{
/// INTERNAL ONLY
///
typedef mpl::always<accumulators::impl::reference_accumulator_impl<Referent, Tag> > impl;
};
}
namespace extract
{
BOOST_ACCUMULATORS_DEFINE_EXTRACTOR(tag, reference, (typename)(typename))
BOOST_ACCUMULATORS_DEFINE_EXTRACTOR(tag, reference_tag, (typename))
}
using extract::reference;
using extract::reference_tag;
// Map all reference<V,T> features to reference_tag<T> so
// that references can be extracted using reference_tag<T>
// without specifying the referent type.
template<typename ValueType, typename Tag>
struct feature_of<tag::reference<ValueType, Tag> >
: feature_of<tag::reference_tag<Tag> >
{
};
}} // namespace boost::accumulators
#endif
@@ -0,0 +1,89 @@
///////////////////////////////////////////////////////////////////////////////
// value_accumulator.hpp
//
// Copyright 2005 Eric Niebler, Daniel Egloff. Distributed under the Boost
// Software License, Version 1.0. (See accompanying file
// LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
#ifndef BOOST_ACCUMULATORS_FRAMEWORK_ACCUMULATORS_VALUE_ACCUMULATOR_HPP_EAN_03_23_2006
#define BOOST_ACCUMULATORS_FRAMEWORK_ACCUMULATORS_VALUE_ACCUMULATOR_HPP_EAN_03_23_2006
#include <boost/mpl/always.hpp>
#include <boost/parameter/keyword.hpp>
#include <boost/accumulators/framework/depends_on.hpp> // for feature_tag
#include <boost/accumulators/framework/accumulator_base.hpp>
#include <boost/accumulators/framework/extractor.hpp>
namespace boost { namespace accumulators
{
namespace impl
{
//////////////////////////////////////////////////////////////////////////
// value_accumulator_impl
template<typename ValueType, typename Tag>
struct value_accumulator_impl
: accumulator_base
{
typedef ValueType result_type;
template<typename Args>
value_accumulator_impl(Args const &args)
: val(args[parameter::keyword<Tag>::instance])
{
}
result_type result(dont_care) const
{
return this->val;
}
private:
ValueType val;
};
} // namespace impl
namespace tag
{
//////////////////////////////////////////////////////////////////////////
// value_tag
template<typename Tag>
struct value_tag
{
};
//////////////////////////////////////////////////////////////////////////
// value
template<typename ValueType, typename Tag>
struct value
: depends_on<>
{
/// INTERNAL ONLY
///
typedef mpl::always<accumulators::impl::value_accumulator_impl<ValueType, Tag> > impl;
};
}
namespace extract
{
BOOST_ACCUMULATORS_DEFINE_EXTRACTOR(tag, value, (typename)(typename))
BOOST_ACCUMULATORS_DEFINE_EXTRACTOR(tag, value_tag, (typename))
}
using extract::value;
using extract::value_tag;
// Map all value<V,T> features to value_tag<T> so
// that values can be extracted using value_tag<T>
// without specifying the value type.
template<typename ValueType, typename Tag>
struct feature_of<tag::value<ValueType, Tag> >
: feature_of<tag::value_tag<Tag> >
{
};
}} // namespace boost::accumulators
#endif
@@ -0,0 +1,461 @@
///////////////////////////////////////////////////////////////////////////////
// depends_on.hpp
//
// Copyright 2005 Eric Niebler. Distributed under the Boost
// Software License, Version 1.0. (See accompanying file
// LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
#ifndef BOOST_ACCUMULATORS_FRAMEWORK_DEPENDS_ON_HPP_EAN_28_10_2005
#define BOOST_ACCUMULATORS_FRAMEWORK_DEPENDS_ON_HPP_EAN_28_10_2005
#include <boost/version.hpp>
#include <boost/mpl/end.hpp>
#include <boost/mpl/map.hpp>
#include <boost/mpl/set.hpp>
#include <boost/mpl/copy.hpp>
#include <boost/mpl/fold.hpp>
#include <boost/mpl/size.hpp>
#include <boost/mpl/sort.hpp>
#include <boost/mpl/insert.hpp>
#include <boost/mpl/assert.hpp>
#include <boost/mpl/remove.hpp>
#include <boost/mpl/vector.hpp>
#include <boost/mpl/inherit.hpp>
#include <boost/mpl/identity.hpp>
#include <boost/mpl/equal_to.hpp>
#include <boost/mpl/contains.hpp>
#include <boost/mpl/transform.hpp>
#include <boost/mpl/is_sequence.hpp>
#include <boost/mpl/placeholders.hpp>
#include <boost/mpl/insert_range.hpp>
#include <boost/mpl/back_inserter.hpp>
#include <boost/mpl/transform_view.hpp>
#include <boost/mpl/inherit_linearly.hpp>
#include <boost/type_traits/is_base_and_derived.hpp>
#include <boost/preprocessor/repetition/repeat.hpp>
#include <boost/preprocessor/repetition/enum_params.hpp>
#include <boost/preprocessor/facilities/intercept.hpp>
#include <boost/accumulators/accumulators_fwd.hpp>
#include <boost/fusion/include/next.hpp>
#include <boost/fusion/include/equal_to.hpp>
#include <boost/fusion/include/value_of.hpp>
#include <boost/fusion/include/mpl.hpp>
#include <boost/fusion/include/end.hpp>
#include <boost/fusion/include/begin.hpp>
#include <boost/fusion/include/cons.hpp>
namespace boost { namespace accumulators
{
///////////////////////////////////////////////////////////////////////////
// as_feature
template<typename Feature>
struct as_feature
{
typedef Feature type;
};
///////////////////////////////////////////////////////////////////////////
// weighted_feature
template<typename Feature>
struct as_weighted_feature
{
typedef Feature type;
};
///////////////////////////////////////////////////////////////////////////
// feature_of
template<typename Feature>
struct feature_of
{
typedef Feature type;
};
namespace detail
{
///////////////////////////////////////////////////////////////////////////
// feature_tag
template<typename Accumulator>
struct feature_tag
{
typedef typename Accumulator::feature_tag type;
};
template<typename Feature>
struct undroppable
{
typedef Feature type;
};
template<typename Feature>
struct undroppable<tag::droppable<Feature> >
{
typedef Feature type;
};
// For the purpose of determining whether one feature depends on another,
// disregard whether the feature is droppable or not.
template<typename A, typename B>
struct is_dependent_on
: is_base_and_derived<
typename feature_of<typename undroppable<B>::type>::type
, typename undroppable<A>::type
>
{};
template<typename Feature>
struct dependencies_of
{
typedef typename Feature::dependencies type;
};
// Should use mpl::insert_range, but doesn't seem to work with mpl sets
template<typename Set, typename Range>
struct set_insert_range
: mpl::fold<
Range
, Set
, mpl::insert<mpl::_1, mpl::_2>
>
{};
template<typename Features>
struct collect_abstract_features
: mpl::fold<
Features
, mpl::set0<>
, set_insert_range<
mpl::insert<mpl::_1, feature_of<mpl::_2> >
, collect_abstract_features<dependencies_of<mpl::_2> >
>
>
{};
template<typename Features>
struct depends_on_base
: mpl::inherit_linearly<
typename mpl::sort<
typename mpl::copy<
typename collect_abstract_features<Features>::type
, mpl::back_inserter<mpl::vector0<> >
>::type
, is_dependent_on<mpl::_1, mpl::_2>
>::type
// Don't inherit multiply from a feature
, mpl::if_<
is_dependent_on<mpl::_1, mpl::_2>
, mpl::_1
, mpl::inherit<mpl::_1, mpl::_2>
>
>::type
{
};
}
///////////////////////////////////////////////////////////////////////////
/// depends_on
template<BOOST_PP_ENUM_PARAMS(BOOST_ACCUMULATORS_MAX_FEATURES, typename Feature)>
struct depends_on
: detail::depends_on_base<
typename mpl::transform<
mpl::vector<BOOST_PP_ENUM_PARAMS(BOOST_ACCUMULATORS_MAX_FEATURES, Feature)>
, as_feature<mpl::_1>
>::type
>
{
typedef mpl::false_ is_weight_accumulator;
typedef
typename mpl::transform<
mpl::vector<BOOST_PP_ENUM_PARAMS(BOOST_ACCUMULATORS_MAX_FEATURES, Feature)>
, as_feature<mpl::_1>
>::type
dependencies;
};
namespace detail
{
template<typename Feature>
struct matches_feature
{
template<typename Accumulator>
struct apply
: is_same<
typename feature_of<typename as_feature<Feature>::type>::type
, typename feature_of<typename as_feature<typename feature_tag<Accumulator>::type>::type>::type
>
{};
};
template<typename Features, typename Accumulator>
struct contains_feature_of
{
typedef
mpl::transform_view<Features, feature_of<as_feature<mpl::_> > >
features_list;
typedef
typename feature_of<typename feature_tag<Accumulator>::type>::type
the_feature;
typedef
typename mpl::contains<features_list, the_feature>::type
type;
};
// This is to work around a bug in early versions of Fusion which caused
// a compile error if contains_feature_of<List, mpl::_> is used as a
// predicate to fusion::find_if
template<typename Features>
struct contains_feature_of_
{
template<typename Accumulator>
struct apply
: contains_feature_of<Features, Accumulator>
{};
};
template<
typename First
, typename Last
, bool is_empty = fusion::result_of::equal_to<First, Last>::value
>
struct build_acc_list;
template<typename First, typename Last>
struct build_acc_list<First, Last, true>
{
typedef fusion::nil_ type;
template<typename Args>
static fusion::nil_
call(Args const &, First const&, Last const&)
{
return fusion::nil_();
}
};
template<typename First, typename Last>
struct build_acc_list<First, Last, false>
{
typedef
build_acc_list<typename fusion::result_of::next<First>::type, Last>
next_build_acc_list;
typedef fusion::cons<
typename fusion::result_of::value_of<First>::type
, typename next_build_acc_list::type>
type;
template<typename Args>
static type
call(Args const &args, First const& f, Last const& l)
{
return type(args, next_build_acc_list::call(args, fusion::next(f), l));
}
};
namespace meta
{
template<typename Sequence>
struct make_acc_list
: build_acc_list<
typename fusion::result_of::begin<Sequence>::type
, typename fusion::result_of::end<Sequence>::type
>
{};
}
template<typename Sequence, typename Args>
typename meta::make_acc_list<Sequence>::type
make_acc_list(Sequence &seq, Args const &args)
{
return meta::make_acc_list<Sequence>::call(args, fusion::begin(seq), fusion::end(seq));
}
template<typename Sequence, typename Args>
typename meta::make_acc_list<Sequence>::type
make_acc_list(Sequence const &seq, Args const &args)
{
return meta::make_acc_list<Sequence const>::call(args, fusion::begin(seq), fusion::end(seq));
}
///////////////////////////////////////////////////////////////////////////
// checked_as_weighted_feature
template<typename Feature>
struct checked_as_weighted_feature
{
typedef typename as_feature<Feature>::type feature_type;
typedef typename as_weighted_feature<feature_type>::type type;
// weighted and non-weighted flavors should provide the same feature.
BOOST_MPL_ASSERT((
is_same<
typename feature_of<feature_type>::type
, typename feature_of<type>::type
>
));
};
///////////////////////////////////////////////////////////////////////////
// as_feature_list
template<typename Features, typename Weight>
struct as_feature_list
: mpl::transform_view<Features, checked_as_weighted_feature<mpl::_1> >
{
};
template<typename Features>
struct as_feature_list<Features, void>
: mpl::transform_view<Features, as_feature<mpl::_1> >
{
};
///////////////////////////////////////////////////////////////////////////
// accumulator_wrapper
template<typename Accumulator, typename Feature>
struct accumulator_wrapper
: Accumulator
{
typedef Feature feature_tag;
accumulator_wrapper(accumulator_wrapper const &that)
: Accumulator(*static_cast<Accumulator const *>(&that))
{
}
accumulator_wrapper& operator=(accumulator_wrapper const &that)
{
*static_cast<Accumulator *>(this) = *static_cast<Accumulator const *>(&that);
return *this;
}
template<typename Args>
accumulator_wrapper(Args const &args)
: Accumulator(args)
{
}
};
///////////////////////////////////////////////////////////////////////////
// to_accumulator
template<typename Feature, typename Sample, typename Weight>
struct to_accumulator
{
typedef
accumulator_wrapper<
typename mpl::apply2<typename Feature::impl, Sample, Weight>::type
, Feature
>
type;
};
template<typename Feature, typename Sample, typename Weight, typename Tag, typename AccumulatorSet>
struct to_accumulator<Feature, Sample, tag::external<Weight, Tag, AccumulatorSet> >
{
BOOST_MPL_ASSERT((is_same<Tag, void>));
BOOST_MPL_ASSERT((is_same<AccumulatorSet, void>));
typedef
accumulator_wrapper<
typename mpl::apply2<typename Feature::impl, Sample, Weight>::type
, Feature
>
accumulator_type;
typedef
typename mpl::if_<
typename Feature::is_weight_accumulator
, accumulator_wrapper<impl::external_impl<accumulator_type, tag::weights>, Feature>
, accumulator_type
>::type
type;
};
// BUGBUG work around an MPL bug wrt map insertion
template<typename FeatureMap, typename Feature>
struct insert_feature
: mpl::eval_if<
mpl::has_key<FeatureMap, typename feature_of<Feature>::type>
, mpl::identity<FeatureMap>
, mpl::insert<FeatureMap, mpl::pair<typename feature_of<Feature>::type, Feature> >
>
{
};
template<typename FeatureMap, typename Feature, typename Weight>
struct insert_dependencies
: mpl::fold<
as_feature_list<typename Feature::dependencies, Weight>
, FeatureMap
, insert_dependencies<
insert_feature<mpl::_1, mpl::_2>
, mpl::_2
, Weight
>
>
{
};
template<typename FeatureMap, typename Features, typename Weight>
struct insert_sequence
: mpl::fold< // BUGBUG should use insert_range, but doesn't seem to work for maps
as_feature_list<Features, Weight>
, FeatureMap
, insert_feature<mpl::_1, mpl::_2>
>
{
};
template<typename Features, typename Sample, typename Weight>
struct make_accumulator_tuple
{
typedef
typename mpl::fold<
as_feature_list<Features, Weight>
, mpl::map0<>
, mpl::if_<
mpl::is_sequence<mpl::_2>
, insert_sequence<mpl::_1, mpl::_2, Weight>
, insert_feature<mpl::_1, mpl::_2>
>
>::type
feature_map;
// for each element in the map, add its dependencies also
typedef
typename mpl::fold<
feature_map
, feature_map
, insert_dependencies<mpl::_1, mpl::second<mpl::_2>, Weight>
>::type
feature_map_with_dependencies;
// turn the map into a vector so we can sort it
typedef
typename mpl::insert_range<
mpl::vector<>
, mpl::end<mpl::vector<> >::type
, mpl::transform_view<feature_map_with_dependencies, mpl::second<mpl::_1> >
>::type
feature_vector_with_dependencies;
// sort the features according to which is derived from which
typedef
typename mpl::sort<
feature_vector_with_dependencies
, is_dependent_on<mpl::_2, mpl::_1>
>::type
sorted_feature_vector;
// From the vector of features, construct a vector of accumulators
typedef
typename mpl::transform<
sorted_feature_vector
, to_accumulator<mpl::_1, Sample, Weight>
>::type
type;
};
} // namespace detail
}} // namespace boost::accumulators
#endif
@@ -0,0 +1,27 @@
///////////////////////////////////////////////////////////////////////////////
// external.hpp
//
// Copyright 2005 Eric Niebler. Distributed under the Boost
// Software License, Version 1.0. (See accompanying file
// LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
#ifndef BOOST_ACCUMULATORS_FRAMEWORK_EXTERNAL_HPP_EAN_01_12_2005
#define BOOST_ACCUMULATORS_FRAMEWORK_EXTERNAL_HPP_EAN_01_12_2005
#include <boost/mpl/apply.hpp>
#include <boost/accumulators/framework/accumulators/external_accumulator.hpp>
//namespace boost { namespace accumulators
//{
//
/////////////////////////////////////////////////////////////////////////////////
//// external
////
//template<typename Type>
//struct external
//{
//};
//
//}} // namespace boost::accumulators
#endif
@@ -0,0 +1,248 @@
///////////////////////////////////////////////////////////////////////////////
// extractor.hpp
//
// Copyright 2005 Eric Niebler. Distributed under the Boost
// Software License, Version 1.0. (See accompanying file
// LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
#ifndef BOOST_ACCUMULATORS_FRAMEWORK_EXTRACTOR_HPP_EAN_28_10_2005
#define BOOST_ACCUMULATORS_FRAMEWORK_EXTRACTOR_HPP_EAN_28_10_2005
#include <boost/preprocessor/cat.hpp>
#include <boost/preprocessor/tuple/rem.hpp>
#include <boost/preprocessor/array/size.hpp>
#include <boost/preprocessor/array/data.hpp>
#include <boost/preprocessor/array/elem.hpp>
#include <boost/preprocessor/seq/to_array.hpp>
#include <boost/preprocessor/seq/transform.hpp>
#include <boost/preprocessor/repetition/enum_params.hpp>
#include <boost/preprocessor/repetition/enum_trailing.hpp>
#include <boost/preprocessor/repetition/enum_trailing_params.hpp>
#include <boost/preprocessor/repetition/enum_trailing_binary_params.hpp>
#include <boost/preprocessor/repetition/repeat.hpp>
#include <boost/preprocessor/repetition/repeat_from_to.hpp>
#include <boost/parameter/binding.hpp>
#include <boost/mpl/bool.hpp>
#include <boost/mpl/if.hpp>
#include <boost/mpl/eval_if.hpp>
#include <boost/mpl/apply.hpp>
#include <boost/type_traits/remove_const.hpp>
#include <boost/type_traits/remove_reference.hpp>
#include <boost/accumulators/accumulators_fwd.hpp>
#include <boost/accumulators/framework/parameters/accumulator.hpp>
namespace boost { namespace accumulators
{
namespace detail
{
template<typename AccumulatorSet, typename Feature>
struct accumulator_set_result
{
typedef typename as_feature<Feature>::type feature_type;
typedef typename mpl::apply<
typename boost::remove_const<
typename boost::remove_reference<AccumulatorSet>::type
>::type
, feature_type
>::type::result_type type;
};
template<typename Args, typename Feature>
struct argument_pack_result
: accumulator_set_result<
typename boost::remove_reference<
typename parameter::binding<
typename boost::remove_const<
typename boost::remove_reference<Args>::type
>::type
, tag::accumulator
>::type
>::type
, Feature
>
{
};
template<typename A, typename Feature>
struct extractor_result
: mpl::eval_if<
detail::is_accumulator_set<A>
, accumulator_set_result<A, Feature>
, argument_pack_result<A, Feature>
>
{
};
template<typename Feature, typename AccumulatorSet>
typename extractor_result<AccumulatorSet, Feature>::type
do_extract(AccumulatorSet const &acc, mpl::true_)
{
typedef typename as_feature<Feature>::type feature_type;
return extract_result<feature_type>(acc);
}
template<typename Feature, typename Args>
typename extractor_result<Args, Feature>::type
do_extract(Args const &args, mpl::false_)
{
typedef typename as_feature<Feature>::type feature_type;
return find_accumulator<feature_type>(args[accumulator]).result(args);
}
} // namespace detail
///////////////////////////////////////////////////////////////////////////////
/// Extracts the result associated with Feature from the specified accumulator_set.
template<typename Feature>
struct extractor
{
typedef extractor<Feature> this_type;
/// The result meta-function for determining the return type of the extractor
template<typename F>
struct result;
template<typename A1>
struct result<this_type(A1)>
: detail::extractor_result<A1, Feature>
{
};
/// Extract the result associated with Feature from the accumulator set
/// \param acc The accumulator set object from which to extract the result
template<typename Arg1>
typename detail::extractor_result<Arg1, Feature>::type
operator ()(Arg1 const &arg1) const
{
// Arg1 could be an accumulator_set or an argument pack containing
// an accumulator_set. Dispatch accordingly.
return detail::do_extract<Feature>(arg1, detail::is_accumulator_set<Arg1>());
}
/// \overload
///
/// \param a1 Optional named parameter to be passed to the accumulator's result() function.
template<typename AccumulatorSet, typename A1>
typename detail::extractor_result<AccumulatorSet, Feature>::type
operator ()(AccumulatorSet const &acc, A1 const &a1) const
{
BOOST_MPL_ASSERT((detail::is_accumulator_set<AccumulatorSet>));
typedef typename as_feature<Feature>::type feature_type;
return extract_result<feature_type>(acc, a1);
}
// ... other overloads generated by Boost.Preprocessor:
/// INTERNAL ONLY
///
#define BOOST_ACCUMULATORS_EXTRACTOR_FUN_OP(z, n, _) \
template<BOOST_PP_ENUM_PARAMS_Z(z, n, typename A)> \
struct result<this_type(BOOST_PP_ENUM_PARAMS_Z(z, n, A))> \
: detail::extractor_result<A1, Feature> \
{}; \
template< \
typename AccumulatorSet \
BOOST_PP_ENUM_TRAILING_PARAMS_Z(z, n, typename A) \
> \
typename detail::extractor_result<AccumulatorSet, Feature>::type \
operator ()( \
AccumulatorSet const &acc \
BOOST_PP_ENUM_TRAILING_BINARY_PARAMS_Z(z, n, A, const &a) \
) const \
{ \
BOOST_MPL_ASSERT((detail::is_accumulator_set<AccumulatorSet>)); \
typedef typename as_feature<Feature>::type feature_type; \
return extract_result<feature_type>(acc BOOST_PP_ENUM_TRAILING_PARAMS_Z(z, n, a));\
}
BOOST_PP_REPEAT_FROM_TO(
2
, BOOST_PP_INC(BOOST_ACCUMULATORS_MAX_ARGS)
, BOOST_ACCUMULATORS_EXTRACTOR_FUN_OP
, _
)
#undef BOOST_ACCUMULATORS_EXTRACTOR_FUN_OP
#ifdef BOOST_ACCUMULATORS_DOXYGEN_INVOKED
/// \overload
///
template<typename AccumulatorSet, typename A1, typename A2, ...>
typename detail::extractor_result<AccumulatorSet, Feature>::type
operator ()(AccumulatorSet const &acc, A1 const &a1, A2 const &a2, ...);
#endif
};
}} // namespace boost::accumulators
/// INTERNAL ONLY
///
#define BOOST_ACCUMULATORS_ARRAY_REM(Array) \
BOOST_PP_TUPLE_REM_CTOR(BOOST_PP_ARRAY_SIZE(Array), BOOST_PP_ARRAY_DATA(Array))
/// INTERNAL ONLY
///
#define BOOST_ACCUMULATORS_SEQ_REM(Seq) \
BOOST_ACCUMULATORS_ARRAY_REM(BOOST_PP_SEQ_TO_ARRAY(Seq))
/// INTERNAL ONLY
///
#define BOOST_ACCUMULATORS_ARGS_OP(s, data, elem) \
T ## s
/// INTERNAL ONLY
///
#define BOOST_ACCUMULATORS_PARAMS_OP(s, data, elem) \
elem T ## s
/// INTERNAL ONLY
///
#define BOOST_ACCUMULATORS_MAKE_FEATURE(Tag, Feature, ParamsSeq) \
Tag::Feature< \
BOOST_ACCUMULATORS_SEQ_REM( \
BOOST_PP_SEQ_TRANSFORM(BOOST_ACCUMULATORS_ARGS_OP, ~, ParamsSeq) \
) \
>
/// INTERNAL ONLY
///
#define BOOST_ACCUMULATORS_DEFINE_EXTRACTOR_FUN_IMPL(z, n, Tag, Feature, ParamsSeq) \
template< \
BOOST_ACCUMULATORS_SEQ_REM( \
BOOST_PP_SEQ_TRANSFORM(BOOST_ACCUMULATORS_PARAMS_OP, ~, ParamsSeq) \
) \
, typename Arg1 \
BOOST_PP_ENUM_TRAILING_PARAMS_Z(z, n, typename A) \
> \
typename boost::accumulators::detail::extractor_result< \
Arg1 \
, BOOST_ACCUMULATORS_MAKE_FEATURE(Tag, Feature, ParamsSeq) \
>::type \
Feature(Arg1 const &arg1 BOOST_PP_ENUM_TRAILING_BINARY_PARAMS_Z(z, n, A, const &a) ) \
{ \
typedef BOOST_ACCUMULATORS_MAKE_FEATURE(Tag, Feature, ParamsSeq) feature_type; \
return boost::accumulators::extractor<feature_type>()( \
arg1 BOOST_PP_ENUM_TRAILING_PARAMS_Z(z, n, a)); \
}
/// INTERNAL ONLY
///
#define BOOST_ACCUMULATORS_DEFINE_EXTRACTOR_FUN(z, n, _) \
BOOST_ACCUMULATORS_DEFINE_EXTRACTOR_FUN_IMPL( \
z \
, n \
, BOOST_PP_ARRAY_ELEM(0, _) \
, BOOST_PP_ARRAY_ELEM(1, _) \
, BOOST_PP_ARRAY_ELEM(2, _) \
)
#define BOOST_ACCUMULATORS_DEFINE_EXTRACTOR(Tag, Feature, ParamSeq) \
BOOST_PP_REPEAT( \
BOOST_PP_INC(BOOST_ACCUMULATORS_MAX_ARGS) \
, BOOST_ACCUMULATORS_DEFINE_EXTRACTOR_FUN \
, (3, (Tag, Feature, ParamSeq)) \
)
#endif
@@ -0,0 +1,29 @@
///////////////////////////////////////////////////////////////////////////////
// features.hpp
//
// Copyright 2005 Eric Niebler. Distributed under the Boost
// Software License, Version 1.0. (See accompanying file
// LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
#ifndef BOOST_ACCUMULATORS_STATISTICS_STATS_HPP_EAN_08_12_2005
#define BOOST_ACCUMULATORS_STATISTICS_STATS_HPP_EAN_08_12_2005
#include <boost/preprocessor/repetition/enum_params.hpp>
#include <boost/mpl/vector.hpp>
#include <boost/accumulators/accumulators_fwd.hpp>
namespace boost { namespace accumulators
{
///////////////////////////////////////////////////////////////////////////////
// features
//
template<BOOST_PP_ENUM_PARAMS(BOOST_ACCUMULATORS_MAX_FEATURES, typename Feature)>
struct features
: mpl::vector<BOOST_PP_ENUM_PARAMS(BOOST_ACCUMULATORS_MAX_FEATURES, Feature)>
{
};
}} // namespace boost::accumulators
#endif
@@ -0,0 +1,22 @@
///////////////////////////////////////////////////////////////////////////////
// accumulator.hpp
//
// Copyright 2005 Eric Niebler. Distributed under the Boost
// Software License, Version 1.0. (See accompanying file
// LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
#ifndef BOOST_ACCUMULATORS_FRAMEWORK_PARAMETERS_ACCUMULATOR_HPP_EAN_31_10_2005
#define BOOST_ACCUMULATORS_FRAMEWORK_PARAMETERS_ACCUMULATOR_HPP_EAN_31_10_2005
#include <boost/parameter/name.hpp>
#include <boost/accumulators/accumulators_fwd.hpp>
namespace boost { namespace accumulators
{
BOOST_PARAMETER_NAME((accumulator, tag) accumulator)
BOOST_ACCUMULATORS_IGNORE_GLOBAL(accumulator)
}} // namespace boost::accumulators
#endif
@@ -0,0 +1,22 @@
///////////////////////////////////////////////////////////////////////////////
// sample.hpp
//
// Copyright 2005 Eric Niebler. Distributed under the Boost
// Software License, Version 1.0. (See accompanying file
// LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
#ifndef BOOST_ACCUMULATORS_FRAMEWORK_PARAMETERS_SAMPLE_HPP_EAN_31_10_2005
#define BOOST_ACCUMULATORS_FRAMEWORK_PARAMETERS_SAMPLE_HPP_EAN_31_10_2005
#include <boost/parameter/name.hpp>
#include <boost/accumulators/accumulators_fwd.hpp>
namespace boost { namespace accumulators
{
BOOST_PARAMETER_NAME((sample, tag) sample)
BOOST_ACCUMULATORS_IGNORE_GLOBAL(sample)
}} // namespace boost::accumulators
#endif
@@ -0,0 +1,23 @@
///////////////////////////////////////////////////////////////////////////////
// weight.hpp
//
// Copyright 2005 Eric Niebler. Distributed under the Boost
// Software License, Version 1.0. (See accompanying file
// LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
#ifndef BOOST_ACCUMULATORS_FRAMEWORK_PARAMETERS_WEIGHT_HPP_EAN_31_10_2005
#define BOOST_ACCUMULATORS_FRAMEWORK_PARAMETERS_WEIGHT_HPP_EAN_31_10_2005
#include <boost/parameter/name.hpp>
#include <boost/accumulators/accumulators_fwd.hpp>
namespace boost { namespace accumulators
{
// The weight of a single sample
BOOST_PARAMETER_NAME((weight, tag) weight)
BOOST_ACCUMULATORS_IGNORE_GLOBAL(weight)
}} // namespace boost::accumulators
#endif
@@ -0,0 +1,23 @@
///////////////////////////////////////////////////////////////////////////////
// weights.hpp
//
// Copyright 2005 Eric Niebler. Distributed under the Boost
// Software License, Version 1.0. (See accompanying file
// LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
#ifndef BOOST_ACCUMULATORS_FRAMEWORK_PARAMETERS_WEIGHTS_HPP_EAN_28_10_2005
#define BOOST_ACCUMULATORS_FRAMEWORK_PARAMETERS_WEIGHTS_HPP_EAN_28_10_2005
#include <boost/parameter/name.hpp>
#include <boost/accumulators/accumulators_fwd.hpp>
namespace boost { namespace accumulators
{
// The weight accumulator
BOOST_PARAMETER_NAME((weights, tag) weights)
BOOST_ACCUMULATORS_IGNORE_GLOBAL(weights)
}} // namespace boost::accumulators
#endif
@@ -0,0 +1,75 @@
// Copyright David Abrahams 2006. Distributed under the Boost
// Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
#ifndef BOOST_DETAIL_FUNCTION1_DWA200655_HPP
# define BOOST_DETAIL_FUNCTION1_DWA200655_HPP
# include <boost/concept_check.hpp>
# include <boost/type_traits/remove_reference.hpp>
# include <boost/type_traits/add_const.hpp>
# include <boost/mpl/apply.hpp>
namespace boost { namespace detail {
// A utility for creating unary function objects that play nicely with
// boost::result_of and that handle the forwarding problem.
//
// mpl::apply<F, A0>::type is expected to be a stateless function
// object that accepts an argument of type A0&. It is also expected
// to have a nested ::result_type identical to its return type.
template<typename F>
struct function1
{
template<typename Signature>
struct result
{};
template<typename This, typename A0>
struct result<This(A0)>
{
// How adding const to arguments handles rvalues.
//
// if A0 is arg0 is represents actual argument
// -------- ------- --------------------------
// T const & T const const T lvalue
// T & T non-const T lvalue
// T const T const const T rvalue
// T T const non-const T rvalue
typedef typename remove_reference<
typename add_const< A0 >::type
>::type arg0;
typedef typename mpl::apply1<F, arg0>::type impl;
typedef typename impl::result_type type;
};
// Handles mutable lvalues
template<typename A0>
typename result<function1(A0 &)>::type
operator ()(A0 &a0) const
{
typedef typename result<function1(A0 &)>::impl impl;
typedef typename result<function1(A0 &)>::type type;
typedef A0 &arg0;
BOOST_CONCEPT_ASSERT((UnaryFunction<impl, type, arg0>));
//boost::function_requires<UnaryFunctionConcept<impl, type, arg0> >();
return impl()(a0);
}
// Handles const lvalues and all rvalues
template<typename A0>
typename result<function1(A0 const &)>::type
operator ()(A0 const &a0) const
{
typedef typename result<function1(A0 const &)>::impl impl;
typedef typename result<function1(A0 const &)>::type type;
typedef A0 const &arg0;
BOOST_CONCEPT_ASSERT((UnaryFunction<impl, type, arg0>));
//boost::function_requires<UnaryFunctionConcept<impl, type, arg0> >();
return impl()(a0);
}
};
}} // namespace boost::detail
#endif // BOOST_DETAIL_FUNCTION1_DWA200655_HPP
@@ -0,0 +1,10 @@
// Copyright David Abrahams 2006. Distributed under the Boost
// Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
#ifndef BOOST_DETAIL_FUNCTION2_DWA200655_HPP
# define BOOST_DETAIL_FUNCTION2_DWA200655_HPP
# define args (2)
# include <boost/accumulators/numeric/detail/function_n.hpp>
#endif // BOOST_DETAIL_FUNCTION2_DWA200655_HPP
@@ -0,0 +1,10 @@
// Copyright David Abrahams 2006. Distributed under the Boost
// Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
#ifndef BOOST_DETAIL_FUNCTION3_DWA2006514_HPP
# define BOOST_DETAIL_FUNCTION3_DWA2006514_HPP
# define args (3)
# include <boost/accumulators/numeric/detail/function_n.hpp>
#endif // BOOST_DETAIL_FUNCTION3_DWA2006514_HPP
@@ -0,0 +1,10 @@
// Copyright David Abrahams 2006. Distributed under the Boost
// Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
#ifndef BOOST_DETAIL_FUNCTION4_DWA2006514_HPP
# define BOOST_DETAIL_FUNCTION4_DWA2006514_HPP
# define args (4)
# include <boost/accumulators/numeric/detail/function_n.hpp>
#endif // BOOST_DETAIL_FUNCTION4_DWA2006514_HPP
@@ -0,0 +1,148 @@
// Copyright David Abrahams 2006. Distributed under the Boost
// Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
// #include guards intentionally disabled.
// #ifndef BOOST_DETAIL_FUNCTION_N_DWA2006514_HPP
// # define BOOST_DETAIL_FUNCTION_N_DWA2006514_HPP
#include <boost/mpl/void.hpp>
#include <boost/mpl/apply.hpp>
#include <boost/preprocessor/control/if.hpp>
#include <boost/preprocessor/cat.hpp>
#include <boost/preprocessor/punctuation/comma_if.hpp>
#include <boost/preprocessor/repetition/enum_params.hpp>
#include <boost/preprocessor/repetition/enum_trailing_params.hpp>
#include <boost/preprocessor/repetition/repeat.hpp>
#include <boost/preprocessor/seq/fold_left.hpp>
#include <boost/preprocessor/seq/seq.hpp>
#include <boost/preprocessor/seq/for_each.hpp>
#include <boost/preprocessor/seq/for_each_i.hpp>
#include <boost/preprocessor/seq/for_each_product.hpp>
#include <boost/preprocessor/seq/size.hpp>
#include <boost/type_traits/add_const.hpp>
#include <boost/type_traits/remove_reference.hpp>
namespace boost { namespace detail {
# define BOOST_DETAIL_default_arg(z, n, _) \
typedef mpl::void_ BOOST_PP_CAT(arg, n);
# define BOOST_DETAIL_function_arg(z, n, _) \
typedef typename remove_reference< \
typename add_const< BOOST_PP_CAT(A, n) >::type \
>::type BOOST_PP_CAT(arg, n);
#define BOOST_DETAIL_cat_arg_counts(s, state, n) \
BOOST_PP_IF( \
n \
, BOOST_PP_CAT(state, BOOST_PP_CAT(_, n)) \
, state \
) \
/**/
#define function_name \
BOOST_PP_SEQ_FOLD_LEFT( \
BOOST_DETAIL_cat_arg_counts \
, BOOST_PP_CAT(function, BOOST_PP_SEQ_HEAD(args)) \
, BOOST_PP_SEQ_TAIL(args)(0) \
) \
/**/
template<typename F>
struct function_name
{
BOOST_PP_REPEAT(
BOOST_MPL_LIMIT_METAFUNCTION_ARITY
, BOOST_DETAIL_default_arg
, ~
)
template<typename Signature>
struct result {};
#define BOOST_DETAIL_function_result(r, _, n) \
template<typename This BOOST_PP_ENUM_TRAILING_PARAMS(n, typename A)> \
struct result<This(BOOST_PP_ENUM_PARAMS(n, A))> \
{ \
BOOST_PP_REPEAT(n, BOOST_DETAIL_function_arg, ~) \
typedef \
typename BOOST_PP_CAT(mpl::apply, BOOST_MPL_LIMIT_METAFUNCTION_ARITY)<\
F \
BOOST_PP_ENUM_TRAILING_PARAMS( \
BOOST_MPL_LIMIT_METAFUNCTION_ARITY \
, arg \
) \
>::type \
impl; \
typedef typename impl::result_type type; \
}; \
/**/
BOOST_PP_SEQ_FOR_EACH(BOOST_DETAIL_function_result, _, args)
# define arg_type(r, _, i, is_const) \
BOOST_PP_COMMA_IF(i) BOOST_PP_CAT(A, i) BOOST_PP_CAT(const_if, is_const) &
# define result_(r, n, constness) \
typename result< \
function_name( \
BOOST_PP_SEQ_FOR_EACH_I_R(r, arg_type, ~, constness) \
) \
> \
/**/
# define param(r, _, i, is_const) BOOST_PP_COMMA_IF(i) \
BOOST_PP_CAT(A, i) BOOST_PP_CAT(const_if, is_const) & BOOST_PP_CAT(x, i)
# define param_list(r, n, constness) \
BOOST_PP_SEQ_FOR_EACH_I_R(r, param, ~, constness)
# define call_operator(r, constness) \
template<BOOST_PP_ENUM_PARAMS(BOOST_PP_SEQ_SIZE(constness), typename A)> \
result_(r, BOOST_PP_SEQ_SIZE(constness), constness)::type \
operator ()( param_list(r, BOOST_PP_SEQ_SIZE(constness), constness) ) const \
{ \
typedef result_(r, BOOST_PP_SEQ_SIZE(constness), constness)::impl impl; \
return impl()(BOOST_PP_ENUM_PARAMS(BOOST_PP_SEQ_SIZE(constness), x)); \
} \
/**/
# define const_if0
# define const_if1 const
# define bits(z, n, _) ((0)(1))
# define gen_operator(r, _, n) \
BOOST_PP_SEQ_FOR_EACH_PRODUCT_R( \
r \
, call_operator \
, BOOST_PP_REPEAT(n, bits, ~) \
) \
/**/
BOOST_PP_SEQ_FOR_EACH(
gen_operator
, ~
, args
)
# undef bits
# undef const_if1
# undef const_if0
# undef call_operator
# undef param_list
# undef param
# undef result_
# undef default_
# undef arg_type
# undef gen_operator
# undef function_name
# undef args
};
}} // namespace boost::detail
//#endif // BOOST_DETAIL_FUNCTION_N_DWA2006514_HPP
@@ -0,0 +1,20 @@
// Copyright David Abrahams 2006. Distributed under the Boost
// Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
#ifndef BOOST_DETAIL_POD_SINGLETON_DWA200655_HPP
# define BOOST_DETAIL_POD_SINGLETON_DWA200655_HPP
namespace boost { namespace detail {
template<typename T>
struct pod_singleton
{
static T instance;
};
template<typename T>
T pod_singleton<T>::instance;
}} // namespace boost::detail
#endif // BOOST_DETAIL_POD_SINGLETON_DWA200655_HPP
@@ -0,0 +1,537 @@
///////////////////////////////////////////////////////////////////////////////
/// \file functional.hpp
///
// Copyright 2005 Eric Niebler. Distributed under the Boost
// Software License, Version 1.0. (See accompanying file
// LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
#ifndef BOOST_NUMERIC_FUNCTIONAL_HPP_EAN_08_12_2005
#define BOOST_NUMERIC_FUNCTIONAL_HPP_EAN_08_12_2005
#include <limits>
#include <functional>
#include <boost/static_assert.hpp>
#include <boost/mpl/if.hpp>
#include <boost/mpl/and.hpp>
#include <boost/type_traits/remove_const.hpp>
#include <boost/type_traits/add_reference.hpp>
#include <boost/type_traits/is_empty.hpp>
#include <boost/type_traits/is_integral.hpp>
#include <boost/type_traits/is_floating_point.hpp>
#include <boost/utility/enable_if.hpp>
#include <boost/typeof/typeof.hpp>
#include <boost/accumulators/accumulators_fwd.hpp>
#include <boost/accumulators/numeric/functional_fwd.hpp>
#include <boost/accumulators/numeric/detail/function1.hpp>
#include <boost/accumulators/numeric/detail/function2.hpp>
#include <boost/accumulators/numeric/detail/pod_singleton.hpp>
#ifdef BOOST_NUMERIC_FUNCTIONAL_STD_VECTOR_SUPPORT
# include <boost/accumulators/numeric/functional/vector.hpp>
#endif
#ifdef BOOST_NUMERIC_FUNCTIONAL_STD_VALARRAY_SUPPORT
# include <boost/accumulators/numeric/functional/valarray.hpp>
#endif
#ifdef BOOST_NUMERIC_FUNCTIONAL_STD_COMPLEX_SUPPORT
# include <boost/accumulators/numeric/functional/complex.hpp>
#endif
/// INTERNAL ONLY
///
#define BOOST_NUMERIC_FUNCTIONAL_HPP_INCLUDED
#ifdef BOOST_NUMERIC_FUNCTIONAL_DOXYGEN_INVOKED
// Hack to make Doxygen show the inheritance relationships
/// INTERNAL ONLY
///
namespace std
{
/// INTERNAL ONLY
///
template<class Arg, class Ret> struct unary_function {};
/// INTERNAL ONLY
///
template<class Left, class Right, class Ret> struct binary_function {};
}
#endif
namespace boost { namespace numeric
{
namespace functional
{
/// INTERNAL ONLY
///
template<typename A0, typename A1>
struct are_integral
: mpl::and_<is_integral<A0>, is_integral<A1> >
{};
template<typename Left, typename Right>
struct left_ref
{
typedef Left &type;
};
namespace detail
{
template<typename T>
T &lvalue_of();
}
}
// TODO: handle complex weight, valarray, MTL vectors
/// INTERNAL ONLY
///
#define BOOST_NUMERIC_FUNCTIONAL_DEFINE_UNARY_OP(Name, Op) \
namespace functional \
{ \
template<typename Arg> \
struct result_of_ ## Name \
{ \
BOOST_TYPEOF_NESTED_TYPEDEF_TPL( \
nested \
, Op boost::numeric::functional::detail::lvalue_of<Arg>() \
) \
typedef typename nested::type type; \
}; \
template<typename Arg, typename EnableIf> \
struct Name ## _base \
{ \
typedef typename remove_const<Arg>::type argument_type; \
typedef typename result_of_ ## Name<Arg>::type result_type; \
typename result_of_ ## Name<Arg>::type operator ()(Arg &arg) const \
{ \
return Op arg; \
} \
}; \
template<typename Arg, typename ArgTag> \
struct Name \
: Name ## _base<Arg, void> \
{}; \
} \
namespace op \
{ \
struct Name \
: boost::detail::function1<functional::Name<_, functional::tag<_> > > \
{}; \
} \
namespace \
{ \
op::Name const &Name = boost::detail::pod_singleton<op::Name>::instance; \
} \
/**/
/// INTERNAL ONLY
///
#define BOOST_NUMERIC_FUNCTIONAL_DEFINE_BINARY_OP(Name, Op, RetType) \
namespace functional \
{ \
template<typename Left, typename Right, typename EnableIf> \
struct result_of_ ## Name \
{ \
RetType(Left, Op, Right) \
}; \
template<typename Left, typename Right, typename EnableIf> \
struct Name ## _base \
{ \
typedef typename remove_const<Left>::type first_argument_type; \
typedef typename remove_const<Right>::type second_argument_type; \
typedef typename result_of_ ## Name<Left, Right>::type result_type; \
typename result_of_ ## Name<Left, Right>::type \
operator ()(Left &left, Right &right) const \
{ \
return left Op right; \
} \
}; \
template<typename Left, typename Right, typename LeftTag, typename RightTag> \
struct Name \
: Name ## _base<Left, Right, void> \
{}; \
} \
namespace op \
{ \
struct Name \
: boost::detail::function2< \
functional::Name<_1, _2, functional::tag<_1>, functional::tag<_2> > \
> \
{}; \
} \
namespace \
{ \
op::Name const &Name = boost::detail::pod_singleton<op::Name>::instance; \
} \
BOOST_ACCUMULATORS_IGNORE_GLOBAL(Name) \
/**/
/// INTERNAL ONLY
///
#define BOOST_NUMERIC_FUNCTIONAL_DEDUCED(Left, Op, Right) \
BOOST_TYPEOF_NESTED_TYPEDEF_TPL( \
nested \
, boost::numeric::functional::detail::lvalue_of<Left>() Op \
boost::numeric::functional::detail::lvalue_of<Right>() \
) \
typedef typename nested::type type; \
/**/
/// INTERNAL ONLY
///
#define BOOST_NUMERIC_FUNCTIONAL_LEFT(Left, Op, Right) \
typedef Left &type; \
/**/
BOOST_NUMERIC_FUNCTIONAL_DEFINE_BINARY_OP(plus, +, BOOST_NUMERIC_FUNCTIONAL_DEDUCED)
BOOST_NUMERIC_FUNCTIONAL_DEFINE_BINARY_OP(minus, -, BOOST_NUMERIC_FUNCTIONAL_DEDUCED)
BOOST_NUMERIC_FUNCTIONAL_DEFINE_BINARY_OP(multiplies, *, BOOST_NUMERIC_FUNCTIONAL_DEDUCED)
BOOST_NUMERIC_FUNCTIONAL_DEFINE_BINARY_OP(divides, /, BOOST_NUMERIC_FUNCTIONAL_DEDUCED)
BOOST_NUMERIC_FUNCTIONAL_DEFINE_BINARY_OP(modulus, %, BOOST_NUMERIC_FUNCTIONAL_DEDUCED)
BOOST_NUMERIC_FUNCTIONAL_DEFINE_BINARY_OP(greater, >, BOOST_NUMERIC_FUNCTIONAL_DEDUCED)
BOOST_NUMERIC_FUNCTIONAL_DEFINE_BINARY_OP(greater_equal, >=, BOOST_NUMERIC_FUNCTIONAL_DEDUCED)
BOOST_NUMERIC_FUNCTIONAL_DEFINE_BINARY_OP(less, <, BOOST_NUMERIC_FUNCTIONAL_DEDUCED)
BOOST_NUMERIC_FUNCTIONAL_DEFINE_BINARY_OP(less_equal, <=, BOOST_NUMERIC_FUNCTIONAL_DEDUCED)
BOOST_NUMERIC_FUNCTIONAL_DEFINE_BINARY_OP(equal_to, ==, BOOST_NUMERIC_FUNCTIONAL_DEDUCED)
BOOST_NUMERIC_FUNCTIONAL_DEFINE_BINARY_OP(not_equal_to, !=, BOOST_NUMERIC_FUNCTIONAL_DEDUCED)
BOOST_NUMERIC_FUNCTIONAL_DEFINE_BINARY_OP(assign, =, BOOST_NUMERIC_FUNCTIONAL_LEFT)
BOOST_NUMERIC_FUNCTIONAL_DEFINE_BINARY_OP(plus_assign, +=, BOOST_NUMERIC_FUNCTIONAL_LEFT)
BOOST_NUMERIC_FUNCTIONAL_DEFINE_BINARY_OP(minus_assign, -=, BOOST_NUMERIC_FUNCTIONAL_LEFT)
BOOST_NUMERIC_FUNCTIONAL_DEFINE_BINARY_OP(multiplies_assign, *=, BOOST_NUMERIC_FUNCTIONAL_LEFT)
BOOST_NUMERIC_FUNCTIONAL_DEFINE_BINARY_OP(divides_assign, /=, BOOST_NUMERIC_FUNCTIONAL_LEFT)
BOOST_NUMERIC_FUNCTIONAL_DEFINE_BINARY_OP(modulus_assign, %=, BOOST_NUMERIC_FUNCTIONAL_LEFT)
BOOST_NUMERIC_FUNCTIONAL_DEFINE_UNARY_OP(unary_plus, +)
BOOST_NUMERIC_FUNCTIONAL_DEFINE_UNARY_OP(unary_minus, -)
BOOST_NUMERIC_FUNCTIONAL_DEFINE_UNARY_OP(complement, ~)
BOOST_NUMERIC_FUNCTIONAL_DEFINE_UNARY_OP(logical_not, !)
#undef BOOST_NUMERIC_FUNCTIONAL_LEFT
#undef BOOST_NUMERIC_FUNCTIONAL_DEDUCED
#undef BOOST_NUMERIC_FUNCTIONAL_DEFINE_UNARY_OP
#undef BOOST_NUMERIC_FUNCTIONAL_DEFINE_BINARY_OP
namespace functional
{
template<typename Left, typename Right, typename EnableIf>
struct min_assign_base
{
typedef Left first_argument_type;
typedef Right second_argument_type;
typedef void result_type;
void operator ()(Left &left, Right &right) const
{
if(numeric::less(right, left))
{
left = right;
}
}
};
template<typename Left, typename Right, typename EnableIf>
struct max_assign_base
{
typedef Left first_argument_type;
typedef Right second_argument_type;
typedef void result_type;
void operator ()(Left &left, Right &right) const
{
if(numeric::greater(right, left))
{
left = right;
}
}
};
template<typename Left, typename Right, typename EnableIf>
struct fdiv_base
: functional::divides<Left, Right>
{};
// partial specialization that promotes the arguments to double for
// integral division.
template<typename Left, typename Right>
struct fdiv_base<Left, Right, typename enable_if<are_integral<Left, Right> >::type>
: functional::divides<double const, double const>
{};
template<typename To, typename From, typename EnableIf>
struct promote_base
{
typedef From argument_type;
typedef To result_type;
To operator ()(From &from) const
{
return from;
}
};
template<typename ToFrom>
struct promote_base<ToFrom, ToFrom, void>
{
typedef ToFrom argument_type;
typedef ToFrom result_type;
ToFrom &operator ()(ToFrom &tofrom)
{
return tofrom;
}
};
template<typename Arg, typename EnableIf>
struct as_min_base
{
BOOST_STATIC_ASSERT(std::numeric_limits<typename remove_const<Arg>::type>::is_specialized);
typedef Arg argument_type;
typedef typename remove_const<Arg>::type result_type;
typename remove_const<Arg>::type operator ()(Arg &) const
{
return (std::numeric_limits<typename remove_const<Arg>::type>::min)();
}
};
template<typename Arg>
struct as_min_base<Arg, typename enable_if<is_floating_point<Arg> >::type>
{
BOOST_STATIC_ASSERT(std::numeric_limits<typename remove_const<Arg>::type>::is_specialized);
typedef Arg argument_type;
typedef typename remove_const<Arg>::type result_type;
typename remove_const<Arg>::type operator ()(Arg &) const
{
return -(std::numeric_limits<typename remove_const<Arg>::type>::max)();
}
};
template<typename Arg, typename EnableIf>
struct as_max_base
{
BOOST_STATIC_ASSERT(std::numeric_limits<typename remove_const<Arg>::type>::is_specialized);
typedef Arg argument_type;
typedef typename remove_const<Arg>::type result_type;
typename remove_const<Arg>::type operator ()(Arg &) const
{
return (std::numeric_limits<typename remove_const<Arg>::type>::max)();
}
};
template<typename Arg, typename EnableIf>
struct as_zero_base
{
typedef Arg argument_type;
typedef typename remove_const<Arg>::type result_type;
typename remove_const<Arg>::type operator ()(Arg &) const
{
return numeric::zero<typename remove_const<Arg>::type>::value;
}
};
template<typename Arg, typename EnableIf>
struct as_one_base
{
typedef Arg argument_type;
typedef typename remove_const<Arg>::type result_type;
typename remove_const<Arg>::type operator ()(Arg &) const
{
return numeric::one<typename remove_const<Arg>::type>::value;
}
};
template<typename To, typename From, typename ToTag, typename FromTag>
struct promote
: promote_base<To, From, void>
{};
template<typename Left, typename Right, typename LeftTag, typename RightTag>
struct min_assign
: min_assign_base<Left, Right, void>
{};
template<typename Left, typename Right, typename LeftTag, typename RightTag>
struct max_assign
: max_assign_base<Left, Right, void>
{};
template<typename Left, typename Right, typename LeftTag, typename RightTag>
struct fdiv
: fdiv_base<Left, Right, void>
{};
/// INTERNAL ONLY
/// For back-compat only. Use fdiv.
template<typename Left, typename Right, typename LeftTag, typename RightTag>
struct average
: fdiv<Left, Right, LeftTag, RightTag>
{};
template<typename Arg, typename Tag>
struct as_min
: as_min_base<Arg, void>
{};
template<typename Arg, typename Tag>
struct as_max
: as_max_base<Arg, void>
{};
template<typename Arg, typename Tag>
struct as_zero
: as_zero_base<Arg, void>
{};
template<typename Arg, typename Tag>
struct as_one
: as_one_base<Arg, void>
{};
}
namespace op
{
template<typename To>
struct promote
: boost::detail::function1<functional::promote<To, _, typename functional::tag<To>::type, functional::tag<_> > >
{};
struct min_assign
: boost::detail::function2<functional::min_assign<_1, _2, functional::tag<_1>, functional::tag<_2> > >
{};
struct max_assign
: boost::detail::function2<functional::max_assign<_1, _2, functional::tag<_1>, functional::tag<_2> > >
{};
struct fdiv
: boost::detail::function2<functional::fdiv<_1, _2, functional::tag<_1>, functional::tag<_2> > >
{};
/// INTERNAL ONLY
struct average
: boost::detail::function2<functional::fdiv<_1, _2, functional::tag<_1>, functional::tag<_2> > >
{};
struct as_min
: boost::detail::function1<functional::as_min<_, functional::tag<_> > >
{};
struct as_max
: boost::detail::function1<functional::as_max<_, functional::tag<_> > >
{};
struct as_zero
: boost::detail::function1<functional::as_zero<_, functional::tag<_> > >
{};
struct as_one
: boost::detail::function1<functional::as_one<_, functional::tag<_> > >
{};
}
namespace
{
op::min_assign const &min_assign = boost::detail::pod_singleton<op::min_assign>::instance;
op::max_assign const &max_assign = boost::detail::pod_singleton<op::max_assign>::instance;
op::fdiv const &fdiv = boost::detail::pod_singleton<op::fdiv>::instance;
op::fdiv const &average = boost::detail::pod_singleton<op::fdiv>::instance; ///< INTERNAL ONLY
op::as_min const &as_min = boost::detail::pod_singleton<op::as_min>::instance;
op::as_max const &as_max = boost::detail::pod_singleton<op::as_max>::instance;
op::as_zero const &as_zero = boost::detail::pod_singleton<op::as_zero>::instance;
op::as_one const &as_one = boost::detail::pod_singleton<op::as_one>::instance;
BOOST_ACCUMULATORS_IGNORE_GLOBAL(min_assign)
BOOST_ACCUMULATORS_IGNORE_GLOBAL(max_assign)
BOOST_ACCUMULATORS_IGNORE_GLOBAL(fdiv)
BOOST_ACCUMULATORS_IGNORE_GLOBAL(average)
BOOST_ACCUMULATORS_IGNORE_GLOBAL(as_min)
BOOST_ACCUMULATORS_IGNORE_GLOBAL(as_max)
BOOST_ACCUMULATORS_IGNORE_GLOBAL(as_zero)
BOOST_ACCUMULATORS_IGNORE_GLOBAL(as_one)
}
///////////////////////////////////////////////////////////////////////////////
// promote
template<typename To, typename From>
typename lazy_disable_if<is_const<From>, mpl::if_<is_same<To, From>, To &, To> >::type
promote(From &from)
{
return functional::promote<To, From>()(from);
}
template<typename To, typename From>
typename mpl::if_<is_same<To const, From const>, To const &, To const>::type
promote(From const &from)
{
return functional::promote<To const, From const>()(from);
}
template<typename T>
struct default_
{
typedef default_ type;
typedef T value_type;
static T const value;
operator T const & () const
{
return default_::value;
}
};
template<typename T>
T const default_<T>::value = T();
template<typename T>
struct one
{
typedef one type;
typedef T value_type;
static T const value;
operator T const & () const
{
return one::value;
}
};
template<typename T>
T const one<T>::value = T(1);
template<typename T>
struct zero
{
typedef zero type;
typedef T value_type;
static T const value;
operator T const & () const
{
return zero::value;
}
};
template<typename T>
T const zero<T>::value = T();
template<typename T>
struct one_or_default
: mpl::if_<is_empty<T>, default_<T>, one<T> >::type
{};
template<typename T>
struct zero_or_default
: mpl::if_<is_empty<T>, default_<T>, zero<T> >::type
{};
}} // namespace boost::numeric
#endif
@@ -0,0 +1,82 @@
///////////////////////////////////////////////////////////////////////////////
/// \file complex.hpp
///
// Copyright 2005 Eric Niebler. Distributed under the Boost
// Software License, Version 1.0. (See accompanying file
// LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
#ifndef BOOST_NUMERIC_FUNCTIONAL_COMPLEX_HPP_EAN_01_17_2006
#define BOOST_NUMERIC_FUNCTIONAL_COMPLEX_HPP_EAN_01_17_2006
#ifdef BOOST_NUMERIC_FUNCTIONAL_HPP_INCLUDED
# error Include this file before boost/accumulators/numeric/functional.hpp
#endif
#include <complex>
#include <boost/mpl/or.hpp>
#include <boost/type_traits/is_same.hpp>
#include <boost/utility/enable_if.hpp>
#include <boost/typeof/std/complex.hpp>
#include <boost/accumulators/numeric/functional_fwd.hpp>
namespace boost { namespace numeric { namespace operators
{
// So that the stats compile when Sample type is std::complex
template<typename T, typename U>
typename
disable_if<
mpl::or_<is_same<T, U>, is_same<std::complex<T>, U> >
, std::complex<T>
>::type
operator *(std::complex<T> ri, U const &u)
{
// BUGBUG promote result to typeof(T()*u) ?
return ri *= static_cast<T>(u);
}
template<typename T, typename U>
typename
disable_if<
mpl::or_<is_same<T, U>, is_same<std::complex<T>, U> >
, std::complex<T>
>::type
operator /(std::complex<T> ri, U const &u)
{
// BUGBUG promote result to typeof(T()*u) ?
return ri /= static_cast<T>(u);
}
}}} // namespace boost::numeric::operators
namespace boost { namespace numeric
{
namespace detail
{
template<typename T>
struct one_complex
{
static std::complex<T> const value;
};
template<typename T>
std::complex<T> const one_complex<T>::value
= std::complex<T>(numeric::one<T>::value, numeric::one<T>::value);
}
/// INTERNAL ONLY
///
template<typename T>
struct one<std::complex<T> >
: detail::one_complex<T>
{
typedef one type;
typedef std::complex<T> value_type;
operator value_type const & () const
{
return detail::one_complex<T>::value;
}
};
}} // namespace boost::numeric
#endif
@@ -0,0 +1,362 @@
///////////////////////////////////////////////////////////////////////////////
/// \file valarray.hpp
///
// Copyright 2005 Eric Niebler. Distributed under the Boost
// Software License, Version 1.0. (See accompanying file
// LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
#ifndef BOOST_NUMERIC_FUNCTIONAL_VALARRAY_HPP_EAN_12_12_2005
#define BOOST_NUMERIC_FUNCTIONAL_VALARRAY_HPP_EAN_12_12_2005
#ifdef BOOST_NUMERIC_FUNCTIONAL_HPP_INCLUDED
# error Include this file before boost/accumulators/numeric/functional.hpp
#endif
#include <valarray>
#include <functional>
#include <boost/assert.hpp>
#include <boost/mpl/and.hpp>
#include <boost/mpl/not.hpp>
#include <boost/mpl/assert.hpp>
#include <boost/utility/enable_if.hpp>
#include <boost/type_traits/is_same.hpp>
#include <boost/type_traits/is_scalar.hpp>
#include <boost/type_traits/remove_const.hpp>
#include <boost/typeof/std/valarray.hpp>
#include <boost/accumulators/numeric/functional_fwd.hpp>
namespace boost { namespace numeric
{
namespace operators
{
namespace acc_detail
{
template<typename Fun>
struct make_valarray
{
typedef std::valarray<typename Fun::result_type> type;
};
}
///////////////////////////////////////////////////////////////////////////////
// Handle valarray<Left> / Right where Right is a scalar and Right != Left.
template<typename Left, typename Right>
typename lazy_enable_if<
mpl::and_<is_scalar<Right>, mpl::not_<is_same<Left, Right> > >
, acc_detail::make_valarray<functional::divides<Left, Right> >
>::type
operator /(std::valarray<Left> const &left, Right const &right)
{
typedef typename functional::divides<Left, Right>::result_type value_type;
std::valarray<value_type> result(left.size());
for(std::size_t i = 0, size = result.size(); i != size; ++i)
{
result[i] = numeric::divides(left[i], right);
}
return result;
}
///////////////////////////////////////////////////////////////////////////////
// Handle valarray<Left> * Right where Right is a scalar and Right != Left.
template<typename Left, typename Right>
typename lazy_enable_if<
mpl::and_<is_scalar<Right>, mpl::not_<is_same<Left, Right> > >
, acc_detail::make_valarray<functional::multiplies<Left, Right> >
>::type
operator *(std::valarray<Left> const &left, Right const &right)
{
typedef typename functional::multiplies<Left, Right>::result_type value_type;
std::valarray<value_type> result(left.size());
for(std::size_t i = 0, size = result.size(); i != size; ++i)
{
result[i] = numeric::multiplies(left[i], right);
}
return result;
}
///////////////////////////////////////////////////////////////////////////////
// Handle valarray<Left> + valarray<Right> where Right != Left.
template<typename Left, typename Right>
typename lazy_disable_if<
is_same<Left, Right>
, acc_detail::make_valarray<functional::plus<Left, Right> >
>::type
operator +(std::valarray<Left> const &left, std::valarray<Right> const &right)
{
typedef typename functional::plus<Left, Right>::result_type value_type;
std::valarray<value_type> result(left.size());
for(std::size_t i = 0, size = result.size(); i != size; ++i)
{
result[i] = numeric::plus(left[i], right[i]);
}
return result;
}
}
namespace functional
{
struct std_valarray_tag;
template<typename T>
struct tag<std::valarray<T> >
{
typedef std_valarray_tag type;
};
#ifdef __GLIBCXX__
template<typename T, typename U>
struct tag<std::_Expr<T, U> >
{
typedef std_valarray_tag type;
};
#endif
/// INTERNAL ONLY
///
// This is necessary because the GCC stdlib uses expression templates, and
// typeof(som-valarray-expression) is not an instance of std::valarray
#define BOOST_NUMERIC_FUNCTIONAL_DEFINE_VALARRAY_BIN_OP(Name, Op) \
template<typename Left, typename Right> \
struct Name<Left, Right, std_valarray_tag, std_valarray_tag> \
{ \
typedef Left first_argument_type; \
typedef Right second_argument_type; \
typedef typename Left::value_type left_value_type; \
typedef typename Right::value_type right_value_type; \
typedef \
std::valarray< \
typename Name<left_value_type, right_value_type>::result_type \
> \
result_type; \
result_type \
operator ()(Left &left, Right &right) const \
{ \
return numeric::promote<std::valarray<left_value_type> >(left) \
Op numeric::promote<std::valarray<right_value_type> >(right); \
} \
}; \
template<typename Left, typename Right> \
struct Name<Left, Right, std_valarray_tag, void> \
{ \
typedef Left first_argument_type; \
typedef Right second_argument_type; \
typedef typename Left::value_type left_value_type; \
typedef \
std::valarray< \
typename Name<left_value_type, Right>::result_type \
> \
result_type; \
result_type \
operator ()(Left &left, Right &right) const \
{ \
return numeric::promote<std::valarray<left_value_type> >(left) Op right;\
} \
}; \
template<typename Left, typename Right> \
struct Name<Left, Right, void, std_valarray_tag> \
{ \
typedef Left first_argument_type; \
typedef Right second_argument_type; \
typedef typename Right::value_type right_value_type; \
typedef \
std::valarray< \
typename Name<Left, right_value_type>::result_type \
> \
result_type; \
result_type \
operator ()(Left &left, Right &right) const \
{ \
return left Op numeric::promote<std::valarray<right_value_type> >(right);\
} \
};
BOOST_NUMERIC_FUNCTIONAL_DEFINE_VALARRAY_BIN_OP(plus, +)
BOOST_NUMERIC_FUNCTIONAL_DEFINE_VALARRAY_BIN_OP(minus, -)
BOOST_NUMERIC_FUNCTIONAL_DEFINE_VALARRAY_BIN_OP(multiplies, *)
BOOST_NUMERIC_FUNCTIONAL_DEFINE_VALARRAY_BIN_OP(divides, /)
BOOST_NUMERIC_FUNCTIONAL_DEFINE_VALARRAY_BIN_OP(modulus, %)
#undef BOOST_NUMERIC_FUNCTIONAL_DEFINE_VALARRAY_BIN_OP
///////////////////////////////////////////////////////////////////////////////
// element-wise min of std::valarray
template<typename Left, typename Right>
struct min_assign<Left, Right, std_valarray_tag, std_valarray_tag>
{
typedef Left first_argument_type;
typedef Right second_argument_type;
typedef void result_type;
void operator ()(Left &left, Right &right) const
{
BOOST_ASSERT(left.size() == right.size());
for(std::size_t i = 0, size = left.size(); i != size; ++i)
{
if(numeric::less(right[i], left[i]))
{
left[i] = right[i];
}
}
}
};
///////////////////////////////////////////////////////////////////////////////
// element-wise max of std::valarray
template<typename Left, typename Right>
struct max_assign<Left, Right, std_valarray_tag, std_valarray_tag>
{
typedef Left first_argument_type;
typedef Right second_argument_type;
typedef void result_type;
void operator ()(Left &left, Right &right) const
{
BOOST_ASSERT(left.size() == right.size());
for(std::size_t i = 0, size = left.size(); i != size; ++i)
{
if(numeric::greater(right[i], left[i]))
{
left[i] = right[i];
}
}
}
};
// partial specialization of numeric::fdiv<> for std::valarray.
template<typename Left, typename Right, typename RightTag>
struct fdiv<Left, Right, std_valarray_tag, RightTag>
: mpl::if_<
are_integral<typename Left::value_type, Right>
, divides<Left, double const>
, divides<Left, Right>
>::type
{};
// promote
template<typename To, typename From>
struct promote<To, From, std_valarray_tag, std_valarray_tag>
{
typedef From argument_type;
typedef To result_type;
To operator ()(From &arr) const
{
typename remove_const<To>::type res(arr.size());
for(std::size_t i = 0, size = arr.size(); i != size; ++i)
{
res[i] = numeric::promote<typename To::value_type>(arr[i]);
}
return res;
}
};
template<typename ToFrom>
struct promote<ToFrom, ToFrom, std_valarray_tag, std_valarray_tag>
{
typedef ToFrom argument_type;
typedef ToFrom result_type;
ToFrom &operator ()(ToFrom &tofrom) const
{
return tofrom;
}
};
// for "promoting" a std::valarray<bool> to a bool, useful for
// comparing 2 valarrays for equality:
// if(numeric::promote<bool>(a == b))
template<typename From>
struct promote<bool, From, void, std_valarray_tag>
{
typedef From argument_type;
typedef bool result_type;
bool operator ()(From &arr) const
{
BOOST_MPL_ASSERT((is_same<bool, typename From::value_type>));
for(std::size_t i = 0, size = arr.size(); i != size; ++i)
{
if(!arr[i])
{
return false;
}
}
return true;
}
};
template<typename From>
struct promote<bool const, From, void, std_valarray_tag>
: promote<bool, From, void, std_valarray_tag>
{};
///////////////////////////////////////////////////////////////////////////////
// functional::as_min
template<typename T>
struct as_min<T, std_valarray_tag>
{
typedef T argument_type;
typedef typename remove_const<T>::type result_type;
typename remove_const<T>::type operator ()(T &arr) const
{
return 0 == arr.size()
? T()
: T(numeric::as_min(arr[0]), arr.size());
}
};
///////////////////////////////////////////////////////////////////////////////
// functional::as_max
template<typename T>
struct as_max<T, std_valarray_tag>
{
typedef T argument_type;
typedef typename remove_const<T>::type result_type;
typename remove_const<T>::type operator ()(T &arr) const
{
return 0 == arr.size()
? T()
: T(numeric::as_max(arr[0]), arr.size());
}
};
///////////////////////////////////////////////////////////////////////////////
// functional::as_zero
template<typename T>
struct as_zero<T, std_valarray_tag>
{
typedef T argument_type;
typedef typename remove_const<T>::type result_type;
typename remove_const<T>::type operator ()(T &arr) const
{
return 0 == arr.size()
? T()
: T(numeric::as_zero(arr[0]), arr.size());
}
};
///////////////////////////////////////////////////////////////////////////////
// functional::as_one
template<typename T>
struct as_one<T, std_valarray_tag>
{
typedef T argument_type;
typedef typename remove_const<T>::type result_type;
typename remove_const<T>::type operator ()(T &arr) const
{
return 0 == arr.size()
? T()
: T(numeric::as_one(arr[0]), arr.size());
}
};
} // namespace functional
}} // namespace boost::numeric
#endif
@@ -0,0 +1,347 @@
///////////////////////////////////////////////////////////////////////////////
/// \file vector.hpp
///
// Copyright 2005 Eric Niebler. Distributed under the Boost
// Software License, Version 1.0. (See accompanying file
// LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
#ifndef BOOST_NUMERIC_FUNCTIONAL_VECTOR_HPP_EAN_12_12_2005
#define BOOST_NUMERIC_FUNCTIONAL_VECTOR_HPP_EAN_12_12_2005
#ifdef BOOST_NUMERIC_FUNCTIONAL_HPP_INCLUDED
# error Include this file before boost/accumulators/numeric/functional.hpp
#endif
#include <vector>
#include <functional>
#include <boost/assert.hpp>
#include <boost/mpl/and.hpp>
#include <boost/mpl/not.hpp>
#include <boost/utility/enable_if.hpp>
#include <boost/type_traits/is_same.hpp>
#include <boost/type_traits/is_scalar.hpp>
#include <boost/type_traits/remove_const.hpp>
#include <boost/typeof/std/vector.hpp>
#include <boost/accumulators/numeric/functional_fwd.hpp>
namespace boost { namespace numeric
{
namespace operators
{
namespace acc_detail
{
template<typename Fun>
struct make_vector
{
typedef std::vector<typename Fun::result_type> type;
};
}
///////////////////////////////////////////////////////////////////////////////
// Handle vector<Left> / Right where Right is a scalar.
template<typename Left, typename Right>
typename lazy_enable_if<
is_scalar<Right>
, acc_detail::make_vector<functional::divides<Left, Right> >
>::type
operator /(std::vector<Left> const &left, Right const &right)
{
typedef typename functional::divides<Left, Right>::result_type value_type;
std::vector<value_type> result(left.size());
for(std::size_t i = 0, size = result.size(); i != size; ++i)
{
result[i] = numeric::divides(left[i], right);
}
return result;
}
///////////////////////////////////////////////////////////////////////////////
// Handle vector<Left> / vector<Right>.
template<typename Left, typename Right>
std::vector<typename functional::divides<Left, Right>::result_type>
operator /(std::vector<Left> const &left, std::vector<Right> const &right)
{
typedef typename functional::divides<Left, Right>::result_type value_type;
std::vector<value_type> result(left.size());
for(std::size_t i = 0, size = result.size(); i != size; ++i)
{
result[i] = numeric::divides(left[i], right[i]);
}
return result;
}
///////////////////////////////////////////////////////////////////////////////
// Handle vector<Left> * Right where Right is a scalar.
template<typename Left, typename Right>
typename lazy_enable_if<
is_scalar<Right>
, acc_detail::make_vector<functional::multiplies<Left, Right> >
>::type
operator *(std::vector<Left> const &left, Right const &right)
{
typedef typename functional::multiplies<Left, Right>::result_type value_type;
std::vector<value_type> result(left.size());
for(std::size_t i = 0, size = result.size(); i != size; ++i)
{
result[i] = numeric::multiplies(left[i], right);
}
return result;
}
///////////////////////////////////////////////////////////////////////////////
// Handle Left * vector<Right> where Left is a scalar.
template<typename Left, typename Right>
typename lazy_enable_if<
is_scalar<Left>
, acc_detail::make_vector<functional::multiplies<Left, Right> >
>::type
operator *(Left const &left, std::vector<Right> const &right)
{
typedef typename functional::multiplies<Left, Right>::result_type value_type;
std::vector<value_type> result(right.size());
for(std::size_t i = 0, size = result.size(); i != size; ++i)
{
result[i] = numeric::multiplies(left, right[i]);
}
return result;
}
///////////////////////////////////////////////////////////////////////////////
// Handle vector<Left> * vector<Right>
template<typename Left, typename Right>
std::vector<typename functional::multiplies<Left, Right>::result_type>
operator *(std::vector<Left> const &left, std::vector<Right> const &right)
{
typedef typename functional::multiplies<Left, Right>::result_type value_type;
std::vector<value_type> result(left.size());
for(std::size_t i = 0, size = result.size(); i != size; ++i)
{
result[i] = numeric::multiplies(left[i], right[i]);
}
return result;
}
///////////////////////////////////////////////////////////////////////////////
// Handle vector<Left> + vector<Right>
template<typename Left, typename Right>
std::vector<typename functional::plus<Left, Right>::result_type>
operator +(std::vector<Left> const &left, std::vector<Right> const &right)
{
typedef typename functional::plus<Left, Right>::result_type value_type;
std::vector<value_type> result(left.size());
for(std::size_t i = 0, size = result.size(); i != size; ++i)
{
result[i] = numeric::plus(left[i], right[i]);
}
return result;
}
///////////////////////////////////////////////////////////////////////////////
// Handle vector<Left> - vector<Right>
template<typename Left, typename Right>
std::vector<typename functional::minus<Left, Right>::result_type>
operator -(std::vector<Left> const &left, std::vector<Right> const &right)
{
typedef typename functional::minus<Left, Right>::result_type value_type;
std::vector<value_type> result(left.size());
for(std::size_t i = 0, size = result.size(); i != size; ++i)
{
result[i] = numeric::minus(left[i], right[i]);
}
return result;
}
///////////////////////////////////////////////////////////////////////////////
// Handle vector<Left> += vector<Left>
template<typename Left>
std::vector<Left> &
operator +=(std::vector<Left> &left, std::vector<Left> const &right)
{
BOOST_ASSERT(left.size() == right.size());
for(std::size_t i = 0, size = left.size(); i != size; ++i)
{
numeric::plus_assign(left[i], right[i]);
}
return left;
}
///////////////////////////////////////////////////////////////////////////////
// Handle -vector<Arg>
template<typename Arg>
std::vector<typename functional::unary_minus<Arg>::result_type>
operator -(std::vector<Arg> const &arg)
{
typedef typename functional::unary_minus<Arg>::result_type value_type;
std::vector<value_type> result(arg.size());
for(std::size_t i = 0, size = result.size(); i != size; ++i)
{
result[i] = numeric::unary_minus(arg[i]);
}
return result;
}
}
namespace functional
{
struct std_vector_tag;
template<typename T, typename Al>
struct tag<std::vector<T, Al> >
{
typedef std_vector_tag type;
};
///////////////////////////////////////////////////////////////////////////////
// element-wise min of std::vector
template<typename Left, typename Right>
struct min_assign<Left, Right, std_vector_tag, std_vector_tag>
{
typedef Left first_argument_type;
typedef Right second_argument_type;
typedef void result_type;
void operator ()(Left &left, Right &right) const
{
BOOST_ASSERT(left.size() == right.size());
for(std::size_t i = 0, size = left.size(); i != size; ++i)
{
if(numeric::less(right[i], left[i]))
{
left[i] = right[i];
}
}
}
};
///////////////////////////////////////////////////////////////////////////////
// element-wise max of std::vector
template<typename Left, typename Right>
struct max_assign<Left, Right, std_vector_tag, std_vector_tag>
{
typedef Left first_argument_type;
typedef Right second_argument_type;
typedef void result_type;
void operator ()(Left &left, Right &right) const
{
BOOST_ASSERT(left.size() == right.size());
for(std::size_t i = 0, size = left.size(); i != size; ++i)
{
if(numeric::greater(right[i], left[i]))
{
left[i] = right[i];
}
}
}
};
// partial specialization for std::vector.
template<typename Left, typename Right>
struct fdiv<Left, Right, std_vector_tag, void>
: mpl::if_<
are_integral<typename Left::value_type, Right>
, divides<Left, double const>
, divides<Left, Right>
>::type
{};
// promote
template<typename To, typename From>
struct promote<To, From, std_vector_tag, std_vector_tag>
{
typedef From argument_type;
typedef To result_type;
To operator ()(From &arr) const
{
typename remove_const<To>::type res(arr.size());
for(std::size_t i = 0, size = arr.size(); i != size; ++i)
{
res[i] = numeric::promote<typename To::value_type>(arr[i]);
}
return res;
}
};
template<typename ToFrom>
struct promote<ToFrom, ToFrom, std_vector_tag, std_vector_tag>
{
typedef ToFrom argument_type;
typedef ToFrom result_type;
ToFrom &operator ()(ToFrom &tofrom) const
{
return tofrom;
}
};
///////////////////////////////////////////////////////////////////////////////
// functional::as_min
template<typename T>
struct as_min<T, std_vector_tag>
{
typedef T argument_type;
typedef typename remove_const<T>::type result_type;
typename remove_const<T>::type operator ()(T &arr) const
{
return 0 == arr.size()
? T()
: T(arr.size(), numeric::as_min(arr[0]));
}
};
///////////////////////////////////////////////////////////////////////////////
// functional::as_max
template<typename T>
struct as_max<T, std_vector_tag>
{
typedef T argument_type;
typedef typename remove_const<T>::type result_type;
typename remove_const<T>::type operator ()(T &arr) const
{
return 0 == arr.size()
? T()
: T(arr.size(), numeric::as_max(arr[0]));
}
};
///////////////////////////////////////////////////////////////////////////////
// functional::as_zero
template<typename T>
struct as_zero<T, std_vector_tag>
{
typedef T argument_type;
typedef typename remove_const<T>::type result_type;
typename remove_const<T>::type operator ()(T &arr) const
{
return 0 == arr.size()
? T()
: T(arr.size(), numeric::as_zero(arr[0]));
}
};
///////////////////////////////////////////////////////////////////////////////
// functional::as_one
template<typename T>
struct as_one<T, std_vector_tag>
{
typedef T argument_type;
typedef typename remove_const<T>::type result_type;
typename remove_const<T>::type operator ()(T &arr) const
{
return 0 == arr.size()
? T()
: T(arr.size(), numeric::as_one(arr[0]));
}
};
} // namespace functional
}} // namespace boost::numeric
#endif
@@ -0,0 +1,221 @@
///////////////////////////////////////////////////////////////////////////////
/// \file functional_fwd.hpp
///
// Copyright 2005 Eric Niebler. Distributed under the Boost
// Software License, Version 1.0. (See accompanying file
// LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
#ifndef BOOST_NUMERIC_FUNCTIONAL_FWD_HPP_EAN_08_12_2005
#define BOOST_NUMERIC_FUNCTIONAL_FWD_HPP_EAN_08_12_2005
#include <boost/mpl/if.hpp>
#include <boost/mpl/placeholders.hpp>
#include <boost/utility/enable_if.hpp>
#include <boost/type_traits/is_same.hpp>
#include <boost/type_traits/is_const.hpp>
namespace boost { namespace numeric
{
// For using directives -- this namespace may be re-opened elsewhere
namespace operators
{}
namespace op
{
using mpl::_;
using mpl::_1;
using mpl::_2;
}
namespace functional
{
using namespace operators;
template<typename T>
struct tag
{
typedef void type;
};
template<typename T>
struct tag<T const>
: tag<T>
{};
template<typename T>
struct tag<T volatile>
: tag<T>
{};
template<typename T>
struct tag<T const volatile>
: tag<T>
{};
template<typename T>
struct static_;
template<typename A0, typename A1>
struct are_integral;
}
/// INTERNAL ONLY
///
#define BOOST_NUMERIC_FUNCTIONAL_DECLARE_UNARY_OP(Name, Op) \
namespace functional \
{ \
template<typename Arg, typename EnableIf = void> \
struct Name ## _base; \
template<typename Arg, typename ArgTag = typename tag<Arg>::type> \
struct Name; \
} \
namespace op \
{ \
struct Name; \
} \
namespace \
{ \
extern op::Name const &Name; \
}
/// INTERNAL ONLY
///
#define BOOST_NUMERIC_FUNCTIONAL_DECLARE_BINARY_OP(Name) \
namespace functional \
{ \
template<typename Left, typename Right, typename EnableIf = void> \
struct result_of_ ## Name; \
template<typename Left, typename Right, typename EnableIf = void> \
struct Name ## _base; \
template< \
typename Left \
, typename Right \
, typename LeftTag = typename tag<Left>::type \
, typename RightTag = typename tag<Right>::type \
> \
struct Name; \
} \
namespace op \
{ \
struct Name; \
} \
namespace \
{ \
extern op::Name const &Name; \
}
BOOST_NUMERIC_FUNCTIONAL_DECLARE_BINARY_OP(plus)
BOOST_NUMERIC_FUNCTIONAL_DECLARE_BINARY_OP(minus)
BOOST_NUMERIC_FUNCTIONAL_DECLARE_BINARY_OP(multiplies)
BOOST_NUMERIC_FUNCTIONAL_DECLARE_BINARY_OP(divides)
BOOST_NUMERIC_FUNCTIONAL_DECLARE_BINARY_OP(modulus)
BOOST_NUMERIC_FUNCTIONAL_DECLARE_BINARY_OP(greater)
BOOST_NUMERIC_FUNCTIONAL_DECLARE_BINARY_OP(greater_equal)
BOOST_NUMERIC_FUNCTIONAL_DECLARE_BINARY_OP(less)
BOOST_NUMERIC_FUNCTIONAL_DECLARE_BINARY_OP(less_equal)
BOOST_NUMERIC_FUNCTIONAL_DECLARE_BINARY_OP(equal_to)
BOOST_NUMERIC_FUNCTIONAL_DECLARE_BINARY_OP(not_equal_to)
BOOST_NUMERIC_FUNCTIONAL_DECLARE_BINARY_OP(assign)
BOOST_NUMERIC_FUNCTIONAL_DECLARE_BINARY_OP(plus_assign)
BOOST_NUMERIC_FUNCTIONAL_DECLARE_BINARY_OP(minus_assign)
BOOST_NUMERIC_FUNCTIONAL_DECLARE_BINARY_OP(multiplies_assign)
BOOST_NUMERIC_FUNCTIONAL_DECLARE_BINARY_OP(divides_assign)
BOOST_NUMERIC_FUNCTIONAL_DECLARE_BINARY_OP(modulus_assign)
BOOST_NUMERIC_FUNCTIONAL_DECLARE_UNARY_OP(unary_plus, +)
BOOST_NUMERIC_FUNCTIONAL_DECLARE_UNARY_OP(unary_minus, -)
BOOST_NUMERIC_FUNCTIONAL_DECLARE_UNARY_OP(complement, ~)
BOOST_NUMERIC_FUNCTIONAL_DECLARE_UNARY_OP(logical_not, !)
#undef BOOST_NUMERIC_FUNCTIONAL_DECLARE_UNARY_OP
#undef BOOST_NUMERIC_FUNCTIONAL_DECLARE_BINARY_OP
namespace functional
{
template<typename To, typename From, typename EnableIf = void>
struct promote_base;
template<typename Left, typename Right, typename EnableIf = void>
struct min_assign_base;
template<typename Left, typename Right, typename EnableIf = void>
struct max_assign_base;
template<typename Left, typename Right, typename EnableIf = void>
struct fdiv_base;
template<typename Arg, typename EnableIf = void>
struct as_min_base;
template<typename Arg, typename EnableIf = void>
struct as_max_base;
template<typename Arg, typename EnableIf = void>
struct as_zero_base;
template<typename Arg, typename EnableIf = void>
struct as_one_base;
template<typename To, typename From, typename ToTag = typename tag<To>::type, typename FromTag = typename tag<From>::type>
struct promote;
template<typename Left, typename Right, typename LeftTag = typename tag<Left>::type, typename RightTag = typename tag<Right>::type>
struct min_assign;
template<typename Left, typename Right, typename LeftTag = typename tag<Left>::type, typename RightTag = typename tag<Right>::type>
struct max_assign;
template<typename Left, typename Right, typename LeftTag = typename tag<Left>::type, typename RightTag = typename tag<Right>::type>
struct fdiv;
template<typename Arg, typename Tag = typename tag<Arg>::type>
struct as_min;
template<typename Arg, typename Tag = typename tag<Arg>::type>
struct as_max;
template<typename Arg, typename Tag = typename tag<Arg>::type>
struct as_zero;
template<typename Arg, typename Tag = typename tag<Arg>::type>
struct as_one;
}
namespace op
{
template<typename To>
struct promote;
struct min_assign;
struct max_assign;
struct fdiv;
struct as_min;
struct as_max;
struct as_zero;
struct as_one;
}
namespace
{
extern op::min_assign const &min_assign;
extern op::max_assign const &max_assign;
extern op::fdiv const &fdiv;
extern op::as_min const &as_min;
extern op::as_max const &as_max;
extern op::as_zero const &as_zero;
extern op::as_one const &as_one;
}
template<typename To, typename From>
typename lazy_disable_if<is_const<From>, mpl::if_<is_same<To, From>, To &, To> >::type
promote(From &from);
template<typename To, typename From>
typename mpl::if_<is_same<To const, From const>, To const &, To const>::type
promote(From const &from);
template<typename T>
struct default_;
template<typename T>
struct one;
template<typename T>
struct zero;
template<typename T>
struct one_or_default;
template<typename T>
struct zero_or_default;
}} // namespace boost::numeric
#endif
+61
View File
@@ -0,0 +1,61 @@
///////////////////////////////////////////////////////////////////////////////
/// \file statistics.hpp
/// Includes all of the Statistical Accumulators Library
//
// Copyright 2005 Eric Niebler. Distributed under the Boost
// Software License, Version 1.0. (See accompanying file
// LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
#ifndef BOOST_ACCUMULATORS_STATISTICS_HPP_EAN_01_17_2006
#define BOOST_ACCUMULATORS_STATISTICS_HPP_EAN_01_17_2006
#include <boost/accumulators/statistics/count.hpp>
#include <boost/accumulators/statistics/covariance.hpp>
#include <boost/accumulators/statistics/density.hpp>
#include <boost/accumulators/statistics/error_of.hpp>
#include <boost/accumulators/statistics/error_of_mean.hpp>
#include <boost/accumulators/statistics/extended_p_square.hpp>
#include <boost/accumulators/statistics/extended_p_square_quantile.hpp>
#include <boost/accumulators/statistics/kurtosis.hpp>
#include <boost/accumulators/statistics/max.hpp>
#include <boost/accumulators/statistics/mean.hpp>
#include <boost/accumulators/statistics/median.hpp>
#include <boost/accumulators/statistics/min.hpp>
#include <boost/accumulators/statistics/moment.hpp>
#include <boost/accumulators/statistics/peaks_over_threshold.hpp>
#include <boost/accumulators/statistics/pot_tail_mean.hpp>
#include <boost/accumulators/statistics/pot_quantile.hpp>
#include <boost/accumulators/statistics/p_square_cumul_dist.hpp>
#include <boost/accumulators/statistics/p_square_quantile.hpp>
#include <boost/accumulators/statistics/skewness.hpp>
#include <boost/accumulators/statistics/stats.hpp>
#include <boost/accumulators/statistics/sum.hpp>
#include <boost/accumulators/statistics/sum_kahan.hpp>
#include <boost/accumulators/statistics/tail.hpp>
#include <boost/accumulators/statistics/tail_quantile.hpp>
#include <boost/accumulators/statistics/tail_mean.hpp>
#include <boost/accumulators/statistics/tail_variate.hpp>
#include <boost/accumulators/statistics/tail_variate_means.hpp>
#include <boost/accumulators/statistics/variance.hpp>
#include <boost/accumulators/statistics/weighted_covariance.hpp>
#include <boost/accumulators/statistics/weighted_density.hpp>
#include <boost/accumulators/statistics/weighted_kurtosis.hpp>
#include <boost/accumulators/statistics/weighted_extended_p_square.hpp>
#include <boost/accumulators/statistics/weighted_mean.hpp>
#include <boost/accumulators/statistics/weighted_median.hpp>
#include <boost/accumulators/statistics/weighted_moment.hpp>
#include <boost/accumulators/statistics/weighted_peaks_over_threshold.hpp>
#include <boost/accumulators/statistics/weighted_p_square_cumul_dist.hpp>
#include <boost/accumulators/statistics/weighted_p_square_quantile.hpp>
#include <boost/accumulators/statistics/weighted_skewness.hpp>
#include <boost/accumulators/statistics/weighted_sum.hpp>
#include <boost/accumulators/statistics/weighted_sum_kahan.hpp>
#include <boost/accumulators/statistics/weighted_tail_quantile.hpp>
#include <boost/accumulators/statistics/weighted_tail_mean.hpp>
#include <boost/accumulators/statistics/weighted_tail_variate_means.hpp>
#include <boost/accumulators/statistics/weighted_variance.hpp>
#include <boost/accumulators/statistics/with_error.hpp>
#include <boost/accumulators/statistics/parameters/quantile_probability.hpp>
#include <boost/accumulators/statistics/variates/covariate.hpp>
#endif

Some files were not shown because too many files have changed in this diff Show More