mirror of
https://github.com/ApfelTeeSaft/Embeddium.git
synced 2026-08-26 19:23:26 +00:00
Initial Release
theres some bugs related to crashes, idk what causes them....
This commit is contained in:
+401
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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.40–10.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.50–19.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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"name": "embeddium",
|
||||
"version": "1.0.0",
|
||||
"dependencies": []
|
||||
}
|
||||
Vendored
+186
@@ -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
|
||||
|
||||
BIN
Binary file not shown.
@@ -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
|
||||
@@ -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
|
||||
+333
@@ -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
|
||||
+108
@@ -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
|
||||
+89
@@ -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
|
||||
+89
@@ -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
|
||||
@@ -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
Reference in New Issue
Block a user