diff --git a/profiler/build/win32/Tracy.vcxproj b/profiler/build/win32/Tracy.vcxproj
index 9438ca89..3ccfa156 100644
--- a/profiler/build/win32/Tracy.vcxproj
+++ b/profiler/build/win32/Tracy.vcxproj
@@ -105,6 +105,7 @@
+
@@ -153,6 +154,7 @@
+
diff --git a/profiler/build/win32/Tracy.vcxproj.filters b/profiler/build/win32/Tracy.vcxproj.filters
index 848251a3..4ba96377 100644
--- a/profiler/build/win32/Tracy.vcxproj.filters
+++ b/profiler/build/win32/Tracy.vcxproj.filters
@@ -84,6 +84,9 @@
common
+
+ server
+
@@ -236,6 +239,9 @@
common
+
+ server
+
diff --git a/server/TracyStorage.cpp b/server/TracyStorage.cpp
new file mode 100644
index 00000000..1ef26190
--- /dev/null
+++ b/server/TracyStorage.cpp
@@ -0,0 +1,106 @@
+#include
+#include
+
+#ifdef _WIN32
+# include
+# include
+#else
+# include
+# include
+# include
+# include
+# include
+#endif
+
+#include "TracyStorage.hpp"
+
+namespace tracy
+{
+
+static bool CreateDirStruct( const std::string& path )
+{
+ struct stat buf;
+ if( stat( path.c_str(), &buf ) == 0 ) return true;
+
+ if( errno != ENOENT )
+ {
+ return false;
+ }
+
+ size_t pos = 0;
+ do
+ {
+ pos = path.find( '/', pos+1 );
+#ifdef _WIN32
+ if( pos == 2 ) continue; // Don't create drive name.
+ if( _mkdir( path.substr( 0, pos ).c_str() ) != 0 )
+#else
+ if( mkdir( path.substr( 0, pos ).c_str(), S_IRWXU ) != 0 )
+#endif
+ {
+ if( errno != EEXIST )
+ {
+ return false;
+ }
+ }
+ }
+ while( pos != std::string::npos );
+
+ return true;
+}
+
+const char* GetSavePath( const char* file )
+{
+ enum { Pool = 8 };
+ enum { MaxPath = 512 };
+ static char bufpool[Pool][MaxPath];
+ static int bufsel = 0;
+ char* buf = bufpool[bufsel];
+ bufsel = ( bufsel + 1 ) % Pool;
+
+#ifdef _WIN32
+ auto path = getenv( "APPDATA" );
+ auto sz = strlen( path );
+ memcpy( buf, path, sz );
+
+ for( size_t i=0; i