diff --git a/src/Prospect.Agent/Prospect.Agent.vcxproj b/src/Prospect.Agent/Prospect.Agent.vcxproj
index 26c6237..a4b111e 100644
--- a/src/Prospect.Agent/Prospect.Agent.vcxproj
+++ b/src/Prospect.Agent/Prospect.Agent.vcxproj
@@ -47,13 +47,21 @@
true
- $(ProjectDir)bin\$(Platform)\$(Configuration)\
+ $(SolutionDir)Prospect.Launcher\Libs
$(ProjectDir)bin\intermediate\$(Platform)\$(Configuration)\
+ $(LibraryPath)
false
- $(ProjectDir)bin\$(Platform)\$(Configuration)\
+ $(SolutionDir)Prospect.Launcher\Libs
$(ProjectDir)bin\intermediate\$(Platform)\$(Configuration)\
+ $(LibraryPath)
+
+
+ true
+
+
+ true
@@ -63,13 +71,13 @@
true
NotUsing
pch.h
- $(ProjectDir)libs\detours\;$(UESDK);%(AdditionalIncludeDirectories)
+ %(AdditionalIncludeDirectories)
Windows
true
false
- detours.lib;%(AdditionalDependencies)
+ %(AdditionalDependencies)
@@ -82,7 +90,8 @@
true
NotUsing
pch.h
- $(ProjectDir)libs\detours\;$(UESDK);%(AdditionalIncludeDirectories)
+ %(AdditionalIncludeDirectories)
+ MinSpace
Windows
@@ -90,12 +99,14 @@
true
true
false
- detours.lib;%(AdditionalDependencies)
+ %(AdditionalDependencies)
+
-
+
+
diff --git a/src/Prospect.Agent/Prospect.Agent.vcxproj.filters b/src/Prospect.Agent/Prospect.Agent.vcxproj.filters
index 9bd498c..4f007b6 100644
--- a/src/Prospect.Agent/Prospect.Agent.vcxproj.filters
+++ b/src/Prospect.Agent/Prospect.Agent.vcxproj.filters
@@ -15,10 +15,16 @@
-
+
Header Files
-
+
+ Header Files
+
+
+ Header Files
+
+
Header Files
diff --git a/src/Prospect.Agent/SDK.h b/src/Prospect.Agent/SDK.h
new file mode 100644
index 0000000..86cea59
--- /dev/null
+++ b/src/Prospect.Agent/SDK.h
@@ -0,0 +1,96 @@
+#pragma once
+
+#include
+#include
+#include
+#include "SDK_Memory.h"
+
+namespace SDK
+{
+ template
+ struct TArray
+ {
+ friend struct FString;
+
+ TArray()
+ {
+ Data = nullptr;
+ Count = Max = 0;
+ }
+
+ int Num() const
+ {
+ return Count;
+ }
+
+ T& operator[](int i)
+ {
+ return Data[i];
+ }
+
+ const T& operator[](int i) const
+ {
+ return Data[i];
+ }
+
+ bool IsValidIndex(int i) const
+ {
+ return i < Num();
+ }
+
+ private:
+ T* Data;
+ int32_t Count;
+ int32_t Max;
+ };
+
+ struct FString : private TArray
+ {
+ FString(const wchar_t* other)
+ {
+ Max = Count = *other ? std::wcslen(other) + 1 : 0;
+
+ if (Count)
+ {
+ Data = static_cast(GMalloc->Malloc(Count));
+
+ wcscpy_s(Data, Count, other);
+ }
+ }
+
+ FString(const std::wstring& other) : FString(other.c_str())
+ {
+
+ }
+
+ bool IsValid() const
+ {
+ return Data != nullptr;
+ }
+
+ const wchar_t* c_str() const
+ {
+ return Data;
+ }
+
+ std::string ToString() const
+ {
+ const auto length = std::wcslen(Data);
+
+ std::string str(length, '\0');
+ std::use_facet>(std::locale()).narrow(Data, Data + length, '?', &str[0]);
+
+ return str;
+ }
+ };
+
+ struct UPlayFabAPISettings
+ {
+ FString VerticalName;
+ FString BaseServiceHost;
+ FString TitleId;
+ FString AdvertisingIdType;
+ FString AdvertisingIdValue;
+ bool DisableAdvertising = false;
+ };
+}
diff --git a/src/Prospect.Agent/SDK_Memory.h b/src/Prospect.Agent/SDK_Memory.h
new file mode 100644
index 0000000..af651a9
--- /dev/null
+++ b/src/Prospect.Agent/SDK_Memory.h
@@ -0,0 +1,79 @@
+#pragma once
+
+#include
+
+#define DEFAULT_ALIGNMENT 0
+
+namespace SDK
+{
+ class FExec
+ {
+ public:
+ virtual ~FExec() = default;
+ private:
+ virtual bool Exec(void* world, void* cmd, void* ar) = 0;
+ };
+
+ class FMalloc : FExec
+ {
+ public:
+ virtual void* Malloc(SIZE_T Count, uint32_t Alignment = DEFAULT_ALIGNMENT) = 0;
+
+ virtual void* TryMalloc(SIZE_T Count, uint32_t Alignment = DEFAULT_ALIGNMENT) = 0;
+
+ virtual void* Realloc(void* Original, SIZE_T Count, uint32_t Alignment = DEFAULT_ALIGNMENT) = 0;
+
+ virtual void* TryRealloc(void* Original, SIZE_T Count, uint32_t Alignment = DEFAULT_ALIGNMENT) = 0;
+
+ virtual void Free(void* Original) = 0;
+
+ virtual SIZE_T QuantizeSize(SIZE_T Count, uint32_t Alignment)
+ {
+ return Count;
+ }
+
+ virtual bool GetAllocationSize(void* Original, SIZE_T& SizeOut)
+ {
+ return false;
+ }
+
+ virtual void Trim(bool bTrimThreadCaches)
+ {
+ }
+
+ virtual void SetupTLSCachesOnCurrentThread()
+ {
+ }
+
+ virtual void ClearAndDisableTLSCachesOnCurrentThread()
+ {
+ }
+
+ virtual void InitializeStatsMetadata();
+
+ virtual bool Exec(void* InWorld, void* Cmd, void* Ar) override
+ {
+ return false;
+ }
+
+ virtual void UpdateStats() = 0;
+
+ virtual void GetAllocatorStats(void* out_Stats) = 0;
+
+ virtual void DumpAllocatorStats(class FOutputDevice& Ar) = 0;
+
+ virtual bool IsInternallyThreadSafe() const
+ {
+ return false;
+ }
+
+ virtual bool ValidateHeap()
+ {
+ return(true);
+ }
+
+ virtual const TCHAR* GetDescriptiveName() = 0;
+ };
+
+ FMalloc* GMalloc;
+}
diff --git a/src/Prospect.Agent/hooks.h b/src/Prospect.Agent/hooks.h
new file mode 100644
index 0000000..4e2a47a
--- /dev/null
+++ b/src/Prospect.Agent/hooks.h
@@ -0,0 +1,3 @@
+#pragma once
+
+#include "SDK.h"
\ No newline at end of file
diff --git a/src/Prospect.Agent/logger.cpp b/src/Prospect.Agent/logger.cpp
index 296a0a2..24eed78 100644
--- a/src/Prospect.Agent/logger.cpp
+++ b/src/Prospect.Agent/logger.cpp
@@ -1,5 +1,8 @@
#include "logger.h"
+#include
+#include
+
HANDLE h_out = nullptr, h_old_out = nullptr;
HANDLE h_err = nullptr, h_old_err = nullptr;
HANDLE h_in = nullptr, h_old_in = nullptr;
diff --git a/src/Prospect.Agent/logger.h b/src/Prospect.Agent/logger.h
index 14ebd2e..9317173 100644
--- a/src/Prospect.Agent/logger.h
+++ b/src/Prospect.Agent/logger.h
@@ -1,8 +1,5 @@
#pragma once
-#include
-#include
-
namespace logger
{
void Attach();
@@ -12,4 +9,4 @@ namespace logger
bool Print(const char* fmt, ...);
char ReadKey();
-};
+}
\ No newline at end of file
diff --git a/src/Prospect.Agent/main.cpp b/src/Prospect.Agent/main.cpp
index 0bf88c3..aa2db4d 100644
--- a/src/Prospect.Agent/main.cpp
+++ b/src/Prospect.Agent/main.cpp
@@ -1,17 +1,71 @@
+#define WIN32_LEAN_AND_MEAN
+#include
+#include
#include
-#include "main.h"
#include "logger.h"
-#include
+#include "SDK.h"
+#include "SDK_Memory.h"
+
+/**
+ * Variable offset, find with "STOPMOVIECAPTURE", scroll up.
+ */
+constexpr uintptr_t OffsetGMalloc = 0x5C9A3F8;
+
+/**
+ * Function offset, find with "?sdk=".
+ */
+constexpr uintptr_t OffsetPlayFabApiGetUrl = 0xCA5010;
+
+/**
+ * Function hooks.
+ */
+typedef SDK::FString(__fastcall* tPlayFabApiGetUrl)(SDK::UPlayFabAPISettings*, const SDK::FString&);
+
+tPlayFabApiGetUrl OldPlayFabApiGetUrl;
+
+SDK::FString __fastcall PlayFabApiGetUrlProxy(SDK::UPlayFabAPISettings* thiz, const SDK::FString& callPath)
+{
+ logger::Print("[Agent] GetURL called with callPath %s\n", callPath.ToString().c_str());
+
+ return std::wstring(L"http://127.0.0.1:8888") + std::wstring(callPath.c_str());
+}
DWORD WINAPI OnDllAttach(LPVOID base)
{
- logger::Attach();
logger::Print("[Agent] DLL Attached\n");
- // TODO: Patch.
+ const auto hModule = GetModuleHandleW(nullptr);
+ const auto hModulePtr = reinterpret_cast(hModule);
- logger::Print("[Agent] DLL Exit\n");
- FreeLibraryAndExitThread(static_cast(base), 1);
+ // Initialize GMalloc.
+ SDK::GMalloc = *reinterpret_cast(hModulePtr + OffsetGMalloc);
+
+ // Initialize MinHook.
+ if (MH_Initialize() != MH_OK)
+ {
+ logger::Print("[Agent] Failed to initialize MinHook\n");
+ FreeLibraryAndExitThread(hModule, 1);
+ }
+
+ // Hook UPlayFabAPISettings::GetUrl.
+ const auto pTarget = reinterpret_cast(hModulePtr + OffsetPlayFabApiGetUrl);
+ const auto ppOriginal = reinterpret_cast(&OldPlayFabApiGetUrl);
+ const auto mhStatus = MH_CreateHook(pTarget, &PlayFabApiGetUrlProxy, ppOriginal);
+
+ if (mhStatus != MH_OK)
+ {
+ logger::Print("[Agent] Failed to create PlayFabAPIGetUrl hook (%d)\n", mhStatus);
+ FreeLibraryAndExitThread(hModule, 1);
+ }
+
+ if (MH_EnableHook(pTarget) != MH_OK)
+ {
+ logger::Print("[Agent] Failed to enable PlayFabAPIGetUrl hook\n");
+ FreeLibraryAndExitThread(hModule, 1);
+ }
+
+ logger::Print("[Agent] DLL Initialized\n");
+ ExitThread(1);
}
BOOL WINAPI DllMain(
@@ -27,10 +81,18 @@ BOOL WINAPI DllMain(
// Allocate a console.
AllocConsole();
freopen_s(reinterpret_cast(stdout), "CONOUT$", "w", stdout);
+
+ // Attach logger.
+ logger::Attach();
// Spawn thread.
CreateThread(nullptr, 0, OnDllAttach, hinstDll, 0, nullptr);
}
+ else if (fdwReason == DLL_PROCESS_DETACH)
+ {
+ // Detach logger.
+ logger::Detach();
+ }
return TRUE;
}
diff --git a/src/Prospect.Agent/main.h b/src/Prospect.Agent/main.h
deleted file mode 100644
index 5cb4cbf..0000000
--- a/src/Prospect.Agent/main.h
+++ /dev/null
@@ -1,4 +0,0 @@
-#pragma once
-
-#define WIN32_LEAN_AND_MEAN
-#include
diff --git a/src/Prospect.Launcher/Inject.cs b/src/Prospect.Launcher/Inject.cs
new file mode 100644
index 0000000..6179c19
--- /dev/null
+++ b/src/Prospect.Launcher/Inject.cs
@@ -0,0 +1,56 @@
+using System;
+using System.Runtime.InteropServices;
+using System.Text;
+using Prospect.Launcher.Invoke;
+
+namespace Prospect.Launcher
+{
+ public static class Inject
+ {
+ const int PROCESS_CREATE_THREAD = 0x0002;
+ const int PROCESS_QUERY_INFORMATION = 0x0400;
+ const int PROCESS_VM_OPERATION = 0x0008;
+ const int PROCESS_VM_WRITE = 0x0020;
+ const int PROCESS_VM_READ = 0x0010;
+
+ const uint MEM_COMMIT = 0x00001000;
+ const uint MEM_RESERVE = 0x00002000;
+ const uint PAGE_READWRITE = 4;
+
+ // https://codingvision.net/c-inject-a-dll-into-a-process-w-createremotethread
+ public static unsafe bool Library(uint processId, string path)
+ {
+ IntPtr procHandle = Kernel32.OpenProcess(PROCESS_CREATE_THREAD | PROCESS_QUERY_INFORMATION | PROCESS_VM_OPERATION | PROCESS_VM_WRITE | PROCESS_VM_READ, false, (int)processId);
+
+ var loadLibraryAddr = Kernel32.GetProcAddress(Kernel32.GetModuleHandle("kernel32.dll"), "LoadLibraryA");
+ if (loadLibraryAddr == IntPtr.Zero)
+ {
+ Console.WriteLine("Failed GetProcAddress.");
+ return false;
+ }
+
+ var allocMemAddress = Kernel32.VirtualAllocEx(procHandle, IntPtr.Zero, (uint)((path.Length + 1) * Marshal.SizeOf(typeof(char))), MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
+ if (allocMemAddress == IntPtr.Zero)
+ {
+ Console.WriteLine("Failed VirtualAllocEx.");
+ return false;
+ }
+
+ var libraryBytes = Encoding.Default.GetBytes(path);
+
+ fixed (byte* pLibraryBytes = libraryBytes)
+ {
+ Kernel32.WriteProcessMemory(procHandle, allocMemAddress, pLibraryBytes, libraryBytes.Length + 1, out _);
+ }
+
+ var threadHandle = Kernel32.CreateRemoteThread(procHandle, IntPtr.Zero, 0, loadLibraryAddr, allocMemAddress, 0, IntPtr.Zero);
+ if (threadHandle == IntPtr.Zero)
+ {
+ Console.WriteLine("Failed CreateRemoteThread.");
+ return false;
+ }
+
+ return true;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Prospect.Launcher/Invoke/Kernel32.cs b/src/Prospect.Launcher/Invoke/Kernel32.cs
index dc58b1e..e2268be 100644
--- a/src/Prospect.Launcher/Invoke/Kernel32.cs
+++ b/src/Prospect.Launcher/Invoke/Kernel32.cs
@@ -42,5 +42,23 @@ namespace Prospect.Launcher.Invoke
[DllImport("kernel32.dll", SetLastError = true)]
public static extern bool VirtualProtectEx(IntPtr hProcess, IntPtr lpAddress, int dwSize, uint flNewProtect, out uint lpflOldProtect);
+
+ [DllImport("kernel32.dll", CharSet = CharSet.Auto)]
+ public static extern IntPtr GetModuleHandle(string lpModuleName);
+
+ [DllImport("kernel32.dll")]
+ public static extern IntPtr OpenProcess(int dwDesiredAccess, bool bInheritHandle, int dwProcessId);
+
+ [DllImport("kernel32", CharSet = CharSet.Ansi, ExactSpelling = true, SetLastError = true)]
+ public static extern IntPtr GetProcAddress(IntPtr hModule, string procName);
+
+ [DllImport("kernel32.dll", SetLastError = true, ExactSpelling = true)]
+ public static extern IntPtr VirtualAllocEx(IntPtr hProcess, IntPtr lpAddress, uint dwSize, uint flAllocationType, uint flProtect);
+
+ [DllImport("kernel32.dll")]
+ public static extern IntPtr CreateRemoteThread(IntPtr hProcess, IntPtr lpThreadAttributes, uint dwStackSize, IntPtr lpStartAddress, IntPtr lpParameter, uint dwCreationFlags, IntPtr lpThreadId);
+
+ [DllImport("kernel32.dll", SetLastError = true)]
+ public static extern uint ResumeThread(IntPtr hThread);
}
}
\ No newline at end of file
diff --git a/src/Prospect.Launcher/Libs/Prospect.Agent.dll b/src/Prospect.Launcher/Libs/Prospect.Agent.dll
new file mode 100644
index 0000000..49738f1
Binary files /dev/null and b/src/Prospect.Launcher/Libs/Prospect.Agent.dll differ
diff --git a/src/Prospect.Launcher/MemoryHelper.cs b/src/Prospect.Launcher/MemoryHelper.cs
index d555494..cd503dd 100644
--- a/src/Prospect.Launcher/MemoryHelper.cs
+++ b/src/Prospect.Launcher/MemoryHelper.cs
@@ -44,10 +44,13 @@ namespace Prospect.Launcher
Write(offset, data);
}
- public unsafe void Write(int offset, Span data)
+ public unsafe void Write(int offset, ReadOnlySpan data)
+ {
+ Write(_baseAddress + offset, data);
+ }
+
+ public unsafe void Write(IntPtr addr, ReadOnlySpan data)
{
- var addr = _baseAddress + offset;
-
// Modify protection.
if (!Kernel32.VirtualProtectEx(_handle, addr, data.Length, 0x40, out var oldProtect))
{
@@ -56,7 +59,7 @@ namespace Prospect.Launcher
fixed (byte* pData = data)
{
- var write = Kernel32.WriteProcessMemory(_handle, _baseAddress + offset, pData, data.Length, out _);
+ var write = Kernel32.WriteProcessMemory(_handle, addr, pData, data.Length, out _);
// Restore protection.
Kernel32.VirtualProtectEx(_handle, addr, data.Length, oldProtect, out _);
diff --git a/src/Prospect.Launcher/Program.cs b/src/Prospect.Launcher/Program.cs
index c01e094..0c53df1 100644
--- a/src/Prospect.Launcher/Program.cs
+++ b/src/Prospect.Launcher/Program.cs
@@ -1,7 +1,6 @@
using System;
using System.Collections.Generic;
using System.IO;
-using System.Threading;
using Prospect.Launcher.Invoke;
using Prospect.Launcher.Invoke.Structs;
@@ -33,6 +32,7 @@ namespace Prospect.Launcher
// 2EA46 = Default
// A22AB = The Cycle Playtest
+ gameArgs.Add("-empty");
gameArgs.Add("-log");
gameArgs.Add("-windowed");
gameArgs.Add("-noeac");
@@ -51,7 +51,7 @@ namespace Prospect.Launcher
// Spawn.
var startupInfo = new StartupInfo();
- if (!Kernel32.CreateProcess(gameBinary, string.Join(' ', gameArgs), ProcessCreationFlags.ZERO_FLAG, ref startupInfo, out var processInfo))
+ if (!Kernel32.CreateProcess(gameBinary, string.Join(' ', gameArgs), ProcessCreationFlags.CREATE_SUSPENDED, ref startupInfo, out var processInfo))
{
Console.WriteLine("Failed to spawn game.");
return;
@@ -59,22 +59,21 @@ namespace Prospect.Launcher
Console.WriteLine("Spawned.");
- // Modify.
- MemoryHelper memory;
-
- while ((memory = MemoryHelper.CreateForHandle(processInfo.hProcess, gameBinary)) == null)
- {
- Console.WriteLine("Failed to obtain MemoryHelper.");
- Thread.Sleep(100);
- }
+ // Inject DLL.
+ var agentPath = Path.Combine(AppContext.BaseDirectory, "Libs", "Prospect.Agent.dll");
- if (!Patches.ChangePlayFabUrl(memory, ".localhost"))
+ if (!Inject.Library(processInfo.dwProcessId, agentPath))
{
- Console.WriteLine("Failed to patch playfab url.");
+ Console.WriteLine("Failed to inject library.");
return;
}
+
+ Console.WriteLine("Injected.");
+
+ // Resume.
+ Kernel32.ResumeThread(processInfo.hThread);
- Console.WriteLine("Patched.");
+ Console.WriteLine("Resumed.");
}
}
}
\ No newline at end of file
diff --git a/src/Prospect.Launcher/Prospect.Launcher.csproj b/src/Prospect.Launcher/Prospect.Launcher.csproj
index a1e2920..3595d26 100644
--- a/src/Prospect.Launcher/Prospect.Launcher.csproj
+++ b/src/Prospect.Launcher/Prospect.Launcher.csproj
@@ -14,4 +14,14 @@
x64
+
+
+
+
+
+
+ PreserveNewest
+
+
+
diff --git a/src/Prospect.Server.Api/Program.cs b/src/Prospect.Server.Api/Program.cs
new file mode 100644
index 0000000..ce0723e
--- /dev/null
+++ b/src/Prospect.Server.Api/Program.cs
@@ -0,0 +1,45 @@
+using System;
+using Microsoft.AspNetCore.Hosting;
+using Microsoft.Extensions.Hosting;
+using Serilog;
+using Serilog.Events;
+
+namespace Prospect.Server.Api
+{
+ public static class Program
+ {
+ public static int Main(string[] args)
+ {
+ Log.Logger = new LoggerConfiguration()
+ .MinimumLevel.Override("Microsoft", LogEventLevel.Information)
+ .MinimumLevel.Override("Microsoft.AspNetCore", LogEventLevel.Warning)
+ .Enrich.FromLogContext()
+ .WriteTo.Console()
+ .CreateLogger();
+
+ try
+ {
+ Log.Information("Starting web host");
+ CreateHostBuilder(args).Build().Run();
+ return 0;
+ }
+ catch (Exception ex)
+ {
+ Log.Fatal(ex, "Host terminated unexpectedly");
+ return 1;
+ }
+ finally
+ {
+ Log.CloseAndFlush();
+ }
+ }
+
+ private static IHostBuilder CreateHostBuilder(string[] args) =>
+ Host.CreateDefaultBuilder(args)
+ .UseSerilog()
+ .ConfigureWebHostDefaults(webBuilder =>
+ {
+ webBuilder.UseStartup();
+ });
+ }
+}
\ No newline at end of file
diff --git a/src/Prospect.Server.Api/Properties/launchSettings.json b/src/Prospect.Server.Api/Properties/launchSettings.json
new file mode 100644
index 0000000..be656e1
--- /dev/null
+++ b/src/Prospect.Server.Api/Properties/launchSettings.json
@@ -0,0 +1,13 @@
+{
+ "profiles": {
+ "Prospect.Server.Api": {
+ "commandName": "Project",
+ "dotnetRunMessages": "true",
+ "launchBrowser": true,
+ "applicationUrl": "https://A22AB.localhost:5555",
+ "environmentVariables": {
+ "ASPNETCORE_ENVIRONMENT": "Development"
+ }
+ }
+ }
+}
diff --git a/src/Prospect.Server.Api/Prospect.Server.Api.csproj b/src/Prospect.Server.Api/Prospect.Server.Api.csproj
new file mode 100644
index 0000000..6fab24b
--- /dev/null
+++ b/src/Prospect.Server.Api/Prospect.Server.Api.csproj
@@ -0,0 +1,11 @@
+
+
+
+ net5.0
+
+
+
+
+
+
+
diff --git a/src/Prospect.Server.Api/Startup.cs b/src/Prospect.Server.Api/Startup.cs
new file mode 100644
index 0000000..de03832
--- /dev/null
+++ b/src/Prospect.Server.Api/Startup.cs
@@ -0,0 +1,33 @@
+using Microsoft.AspNetCore.Builder;
+using Microsoft.AspNetCore.Hosting;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Hosting;
+using Serilog;
+
+namespace Prospect.Server.Api
+{
+ public class Startup
+ {
+ public void ConfigureServices(IServiceCollection services)
+ {
+ services.AddControllers();
+ }
+
+ public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
+ {
+ if (env.IsDevelopment())
+ {
+ app.UseDeveloperExceptionPage();
+ }
+
+ app.UseSerilogRequestLogging();
+
+ app.UseRouting();
+
+ app.UseEndpoints(endpoints =>
+ {
+ endpoints.MapControllers();
+ });
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Prospect.Server.Api/appsettings.Development.json b/src/Prospect.Server.Api/appsettings.Development.json
new file mode 100644
index 0000000..2c63c08
--- /dev/null
+++ b/src/Prospect.Server.Api/appsettings.Development.json
@@ -0,0 +1,2 @@
+{
+}
diff --git a/src/Prospect.Server.Api/appsettings.json b/src/Prospect.Server.Api/appsettings.json
new file mode 100644
index 0000000..2c63c08
--- /dev/null
+++ b/src/Prospect.Server.Api/appsettings.json
@@ -0,0 +1,2 @@
+{
+}
diff --git a/src/Prospect.sln b/src/Prospect.sln
index 4d9c660..d4d1cb2 100644
--- a/src/Prospect.sln
+++ b/src/Prospect.sln
@@ -7,6 +7,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Prospect.Agent", "Prospect.
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Prospect.Launcher", "Prospect.Launcher\Prospect.Launcher.csproj", "{1C8F4637-9552-4726-818B-196ECE2AE966}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Prospect.Server.Api", "Prospect.Server.Api\Prospect.Server.Api.csproj", "{A539B020-8BEC-497F-9176-DC2665D927A1}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|x64 = Debug|x64
@@ -29,6 +31,14 @@ Global
{1C8F4637-9552-4726-818B-196ECE2AE966}.Release|x64.Build.0 = Release|Any CPU
{1C8F4637-9552-4726-818B-196ECE2AE966}.Release|x86.ActiveCfg = Release|Any CPU
{1C8F4637-9552-4726-818B-196ECE2AE966}.Release|x86.Build.0 = Release|Any CPU
+ {A539B020-8BEC-497F-9176-DC2665D927A1}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {A539B020-8BEC-497F-9176-DC2665D927A1}.Debug|x64.Build.0 = Debug|Any CPU
+ {A539B020-8BEC-497F-9176-DC2665D927A1}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {A539B020-8BEC-497F-9176-DC2665D927A1}.Debug|x86.Build.0 = Debug|Any CPU
+ {A539B020-8BEC-497F-9176-DC2665D927A1}.Release|x64.ActiveCfg = Release|Any CPU
+ {A539B020-8BEC-497F-9176-DC2665D927A1}.Release|x64.Build.0 = Release|Any CPU
+ {A539B020-8BEC-497F-9176-DC2665D927A1}.Release|x86.ActiveCfg = Release|Any CPU
+ {A539B020-8BEC-497F-9176-DC2665D927A1}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
diff --git a/src/Prospect.sln.DotSettings b/src/Prospect.sln.DotSettings
new file mode 100644
index 0000000..6f47746
--- /dev/null
+++ b/src/Prospect.sln.DotSettings
@@ -0,0 +1,6 @@
+
+ API
+ SDK
+ <NamingElement Priority="1"><Descriptor Static="Indeterminate" Constexpr="Indeterminate" Const="Indeterminate" Volatile="Indeterminate" Accessibility="NOT_APPLICABLE"><type Name="__interface" /><type Name="class" /><type Name="struct" /></Descriptor><Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /></NamingElement>
+ <NamingElement Priority="10"><Descriptor Static="Indeterminate" Constexpr="Indeterminate" Const="Indeterminate" Volatile="Indeterminate" Accessibility="NOT_APPLICABLE"><type Name="member function" /></Descriptor><Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /></NamingElement>
+ <NamingElement Priority="17"><Descriptor Static="Indeterminate" Constexpr="Indeterminate" Const="Indeterminate" Volatile="Indeterminate" Accessibility="NOT_APPLICABLE"><type Name="namespace" /><type Name="namespace alias" /></Descriptor><Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /></NamingElement>
\ No newline at end of file