diff --git a/capture/build/unix/Makefile b/capture/build/unix/Makefile
new file mode 100644
index 00000000..3b50301c
--- /dev/null
+++ b/capture/build/unix/Makefile
@@ -0,0 +1,12 @@
+all: debug
+
+debug:
+ @+make -f debug.mk all
+
+release:
+ @+make -f release.mk all
+
+clean:
+ @+make -f build.mk clean
+
+.PHONY: all clean debug release
diff --git a/capture/build/unix/build.mk b/capture/build/unix/build.mk
new file mode 100644
index 00000000..4196005b
--- /dev/null
+++ b/capture/build/unix/build.mk
@@ -0,0 +1,51 @@
+CFLAGS +=
+CXXFLAGS := $(CFLAGS) -std=gnu++14
+DEFINES +=
+INCLUDES :=
+LIBS :=
+IMAGE := capture
+
+FILTER :=
+
+BASE := $(shell egrep 'ClCompile.*cpp"' ../win32/$(IMAGE).vcxproj | sed -e 's/.*\"\(.*\)\".*/\1/' | sed -e 's@\\@/@g')
+BASE2 := $(shell egrep 'ClCompile.*c"' ../win32/$(IMAGE).vcxproj | sed -e 's/.*\"\(.*\)\".*/\1/' | sed -e 's@\\@/@g')
+
+SRC := $(filter-out $(FILTER),$(BASE))
+SRC2 := $(filter-out $(FILTER),$(BASE2))
+
+OBJ := $(SRC:%.cpp=%.o)
+OBJ2 := $(SRC2:%.c=%.o)
+
+all: $(IMAGE)
+
+%.o: %.cpp
+ $(CXX) -c $(INCLUDES) $(CXXFLAGS) $(DEFINES) $< -o $@
+
+%.d : %.cpp
+ @echo Resolving dependencies of $<
+ @mkdir -p $(@D)
+ @$(CXX) -MM $(INCLUDES) $(CXXFLAGS) $(DEFINES) $< > $@.$$$$; \
+ sed 's,.*\.o[ :]*,$(<:.cpp=.o) $@ : ,g' < $@.$$$$ > $@; \
+ rm -f $@.$$$$
+
+%.o: %.c
+ $(CC) -c $(INCLUDES) $(CFLAGS) $(DEFINES) $< -o $@
+
+%.d : %.c
+ @echo Resolving dependencies of $<
+ @mkdir -p $(@D)
+ @$(CC) -MM $(INCLUDES) $(CFLAGS) $(DEFINES) $< > $@.$$$$; \
+ sed 's,.*\.o[ :]*,$(<:.c=.o) $@ : ,g' < $@.$$$$ > $@; \
+ rm -f $@.$$$$
+
+$(IMAGE): $(OBJ) $(OBJ2)
+ $(CXX) $(CXXFLAGS) $(DEFINES) $(OBJ) $(OBJ2) $(LIBS) -o $@
+
+ifneq "$(MAKECMDGOALS)" "clean"
+-include $(SRC:.cpp=.d) $(SRC2:.c=.d)
+endif
+
+clean:
+ rm -f $(OBJ) $(OBJ2) $(SRC:.cpp=.d) $(SRC2:.c=.d) $(IMAGE)
+
+.PHONY: clean all
diff --git a/capture/build/unix/debug.mk b/capture/build/unix/debug.mk
new file mode 100644
index 00000000..5a4e17b3
--- /dev/null
+++ b/capture/build/unix/debug.mk
@@ -0,0 +1,10 @@
+ARCH := $(shell uname -m)
+
+CFLAGS := -g3 -Wall
+DEFINES := -DDEBUG
+
+ifeq ($(ARCH),x86_64)
+CFLAGS += -msse4.1
+endif
+
+include build.mk
diff --git a/capture/build/unix/release.mk b/capture/build/unix/release.mk
new file mode 100644
index 00000000..1c820a6e
--- /dev/null
+++ b/capture/build/unix/release.mk
@@ -0,0 +1,10 @@
+ARCH := $(shell uname -m)
+
+CFLAGS := -O3 -s -fomit-frame-pointer
+DEFINES := -DNDEBUG
+
+ifeq ($(ARCH),x86_64)
+CFLAGS += -msse4.1
+endif
+
+include build.mk
diff --git a/capture/build/win32/capture.sln b/capture/build/win32/capture.sln
new file mode 100644
index 00000000..aec6a2e0
--- /dev/null
+++ b/capture/build/win32/capture.sln
@@ -0,0 +1,25 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio 15
+VisualStudioVersion = 15.0.27428.2002
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "capture", "capture.vcxproj", "{447D58BF-94CD-4469-BB90-549C05D03E00}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|x64 = Debug|x64
+ Release|x64 = Release|x64
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {447D58BF-94CD-4469-BB90-549C05D03E00}.Debug|x64.ActiveCfg = Debug|x64
+ {447D58BF-94CD-4469-BB90-549C05D03E00}.Debug|x64.Build.0 = Debug|x64
+ {447D58BF-94CD-4469-BB90-549C05D03E00}.Release|x64.ActiveCfg = Release|x64
+ {447D58BF-94CD-4469-BB90-549C05D03E00}.Release|x64.Build.0 = Release|x64
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {3E51386C-43EA-44AC-9F24-AFAFE4D63ADE}
+ EndGlobalSection
+EndGlobal
diff --git a/capture/build/win32/capture.vcxproj b/capture/build/win32/capture.vcxproj
new file mode 100644
index 00000000..39712627
--- /dev/null
+++ b/capture/build/win32/capture.vcxproj
@@ -0,0 +1,163 @@
+
+
+
+
+ Debug
+ Win32
+
+
+ Release
+ Win32
+
+
+ Debug
+ x64
+
+
+ Release
+ x64
+
+
+
+ 15.0
+ {447D58BF-94CD-4469-BB90-549C05D03E00}
+ capture
+ 10.0.16299.0
+
+
+
+ Application
+ true
+ v141
+ MultiByte
+
+
+ Application
+ false
+ v141
+ true
+ MultiByte
+
+
+ Application
+ true
+ v141
+ MultiByte
+
+
+ Application
+ false
+ v141
+ true
+ MultiByte
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Level3
+ Disabled
+ true
+ true
+
+
+
+
+ Level3
+ Disabled
+ true
+ true
+ true
+ _CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;WIN32_LEAN_AND_MEAN;NOMINMAX;_USE_MATH_DEFINES;%(PreprocessorDefinitions)
+ AdvancedVectorExtensions2
+
+
+ ws2_32.lib;%(AdditionalDependencies)
+ Console
+
+
+
+
+ Level3
+ MaxSpeed
+ true
+ true
+ true
+ true
+
+
+ true
+ true
+
+
+
+
+ Level3
+ MaxSpeed
+ true
+ true
+ true
+ true
+ true
+ NDEBUG;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;WIN32_LEAN_AND_MEAN;NOMINMAX;_USE_MATH_DEFINES;%(PreprocessorDefinitions)
+ AdvancedVectorExtensions2
+
+
+ true
+ true
+ ws2_32.lib;%(AdditionalDependencies)
+ Console
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/capture/build/win32/capture.vcxproj.filters b/capture/build/win32/capture.vcxproj.filters
new file mode 100644
index 00000000..3bc2972c
--- /dev/null
+++ b/capture/build/win32/capture.vcxproj.filters
@@ -0,0 +1,99 @@
+
+
+
+
+ {729c80ee-4d26-4a5e-8f1f-6c075783eb56}
+
+
+ {cf23ef7b-7694-4154-830b-00cf053350ea}
+
+
+ {e39d3623-47cd-4752-8da9-3ea324f964c1}
+
+
+
+
+ common
+
+
+ common
+
+
+ common
+
+
+ server
+
+
+ server
+
+
+ src
+
+
+ src
+
+
+
+
+ common
+
+
+ common
+
+
+ common
+
+
+ common
+
+
+ common
+
+
+ common
+
+
+ common
+
+
+ common
+
+
+ server
+
+
+ server
+
+
+ server
+
+
+ server
+
+
+ server
+
+
+ server
+
+
+ server
+
+
+ server
+
+
+ server
+
+
+ server
+
+
+ server
+
+
+ src
+
+
+
\ No newline at end of file
diff --git a/capture/src/capture.cpp b/capture/src/capture.cpp
new file mode 100644
index 00000000..101c3d9d
--- /dev/null
+++ b/capture/src/capture.cpp
@@ -0,0 +1,97 @@
+#ifdef _WIN32
+# include
+#endif
+
+#include
+#include
+#include
+#include
+
+#include "../../server/tracy_benaphore.h"
+#include "../../server/TracyFileWrite.hpp"
+#include "../../server/TracyMemory.hpp"
+#include "../../server/TracyWorker.hpp"
+#include "getopt.h"
+
+void Usage()
+{
+ printf( "Usage: capture -a address -o output.tracy\n" );
+ exit( 1 );
+}
+
+int main( int argc, char** argv )
+{
+#ifdef _WIN32
+ if( !AttachConsole( ATTACH_PARENT_PROCESS ) )
+ {
+ AllocConsole();
+ SetConsoleMode( GetStdHandle( STD_OUTPUT_HANDLE ), 0x07 );
+ }
+#endif
+
+ const char* address = nullptr;
+ const char* output = nullptr;
+
+ int c;
+ while( ( c = getopt( argc, argv, "a:o:" ) ) != -1 )
+ {
+ switch( c )
+ {
+ case 'a':
+ address = optarg;
+ break;
+ case 'o':
+ output = optarg;
+ break;
+ default:
+ Usage();
+ break;
+ }
+ }
+
+ if( !address || !output ) Usage();
+
+ printf( "Connecting to %s...", address );
+ fflush( stdout );
+ tracy::Worker worker( address );
+ while( !worker.HasData() ) std::this_thread::sleep_for( std::chrono::milliseconds( 100 ) );
+ printf( "\n" );
+
+ auto& lock = worker.GetMbpsDataLock();
+
+ while( worker.IsConnected() )
+ {
+ lock.lock();
+ const auto mbps = worker.GetMbpsData().back();
+ const auto compRatio = worker.GetCompRatio();
+ lock.unlock();
+
+ if( mbps < 0.1f )
+ {
+ printf( "\33[2K\r\033[36;1m%7.2f Kbps", mbps * 1000.f );
+ }
+ else
+ {
+ printf( "\33[2K\r\033[36;1m%7.2f Mbps", mbps );
+ }
+ printf( " \033[0m| Ratio: \033[36;1m%5.1f%% \033[0m| Real: \033[33;1m%7.2f Mbps \033[0m| Mem: \033[31;1m%.2f MB\033[0m", compRatio * 100.f, mbps / compRatio, tracy::memUsage.load( std::memory_order_relaxed ) / ( 1024.f * 1024.f ) );
+ fflush( stdout );
+
+ std::this_thread::sleep_for( std::chrono::milliseconds( 100 ) );
+ }
+
+ printf( "\nSaving trace..." );
+ fflush( stdout );
+ auto f = tracy::FileWrite::Open( output );
+ if( f )
+ {
+ worker.Write( *f );
+ printf( " \033[32;1mdone!\033[0m\n" );
+ }
+ else
+ {
+ printf( " \033[31;1failed!\033[0m\n" );
+ }
+
+ return 0;
+}
diff --git a/capture/src/getopt.c b/capture/src/getopt.c
new file mode 100644
index 00000000..eae36a64
--- /dev/null
+++ b/capture/src/getopt.c
@@ -0,0 +1,228 @@
+/*******************************************************************************
+ * Copyright (c) 2012-2017, Kim Grasman
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * 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.
+ * * Neither the name of Kim Grasman nor the
+ * names of contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * 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 KIM GRASMAN 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.
+ *
+ ******************************************************************************/
+
+#include "getopt.h"
+
+#include
+#include
+
+char* optarg;
+int optopt;
+/* The variable optind [...] shall be initialized to 1 by the system. */
+int optind = 1;
+int opterr;
+
+static char* optcursor = NULL;
+
+/* Implemented based on [1] and [2] for optional arguments.
+ optopt is handled FreeBSD-style, per [3].
+ Other GNU and FreeBSD extensions are purely accidental.
+
+[1] http://pubs.opengroup.org/onlinepubs/000095399/functions/getopt.html
+[2] http://www.kernel.org/doc/man-pages/online/pages/man3/getopt.3.html
+[3] http://www.freebsd.org/cgi/man.cgi?query=getopt&sektion=3&manpath=FreeBSD+9.0-RELEASE
+*/
+int getopt(int argc, char* const argv[], const char* optstring) {
+ int optchar = -1;
+ const char* optdecl = NULL;
+
+ optarg = NULL;
+ opterr = 0;
+ optopt = 0;
+
+ /* Unspecified, but we need it to avoid overrunning the argv bounds. */
+ if (optind >= argc)
+ goto no_more_optchars;
+
+ /* If, when getopt() is called argv[optind] is a null pointer, getopt()
+ shall return -1 without changing optind. */
+ if (argv[optind] == NULL)
+ goto no_more_optchars;
+
+ /* If, when getopt() is called *argv[optind] is not the character '-',
+ getopt() shall return -1 without changing optind. */
+ if (*argv[optind] != '-')
+ goto no_more_optchars;
+
+ /* If, when getopt() is called argv[optind] points to the string "-",
+ getopt() shall return -1 without changing optind. */
+ if (strcmp(argv[optind], "-") == 0)
+ goto no_more_optchars;
+
+ /* If, when getopt() is called argv[optind] points to the string "--",
+ getopt() shall return -1 after incrementing optind. */
+ if (strcmp(argv[optind], "--") == 0) {
+ ++optind;
+ goto no_more_optchars;
+ }
+
+ if (optcursor == NULL || *optcursor == '\0')
+ optcursor = argv[optind] + 1;
+
+ optchar = *optcursor;
+
+ /* FreeBSD: The variable optopt saves the last known option character
+ returned by getopt(). */
+ optopt = optchar;
+
+ /* The getopt() function shall return the next option character (if one is
+ found) from argv that matches a character in optstring, if there is
+ one that matches. */
+ optdecl = strchr(optstring, optchar);
+ if (optdecl) {
+ /* [I]f a character is followed by a colon, the option takes an
+ argument. */
+ if (optdecl[1] == ':') {
+ optarg = ++optcursor;
+ if (*optarg == '\0') {
+ /* GNU extension: Two colons mean an option takes an
+ optional arg; if there is text in the current argv-element
+ (i.e., in the same word as the option name itself, for example,
+ "-oarg"), then it is returned in optarg, otherwise optarg is set
+ to zero. */
+ if (optdecl[2] != ':') {
+ /* If the option was the last character in the string pointed to by
+ an element of argv, then optarg shall contain the next element
+ of argv, and optind shall be incremented by 2. If the resulting
+ value of optind is greater than argc, this indicates a missing
+ option-argument, and getopt() shall return an error indication.
+
+ Otherwise, optarg shall point to the string following the
+ option character in that element of argv, and optind shall be
+ incremented by 1.
+ */
+ if (++optind < argc) {
+ optarg = argv[optind];
+ } else {
+ /* If it detects a missing option-argument, it shall return the
+ colon character ( ':' ) if the first character of optstring
+ was a colon, or a question-mark character ( '?' ) otherwise.
+ */
+ optarg = NULL;
+ optchar = (optstring[0] == ':') ? ':' : '?';
+ }
+ } else {
+ optarg = NULL;
+ }
+ }
+
+ optcursor = NULL;
+ }
+ } else {
+ /* If getopt() encounters an option character that is not contained in
+ optstring, it shall return the question-mark ( '?' ) character. */
+ optchar = '?';
+ }
+
+ if (optcursor == NULL || *++optcursor == '\0')
+ ++optind;
+
+ return optchar;
+
+no_more_optchars:
+ optcursor = NULL;
+ return -1;
+}
+
+/* Implementation based on [1].
+
+[1] http://www.kernel.org/doc/man-pages/online/pages/man3/getopt.3.html
+*/
+int getopt_long(int argc, char* const argv[], const char* optstring,
+ const struct option* longopts, int* longindex) {
+ const struct option* o = longopts;
+ const struct option* match = NULL;
+ int num_matches = 0;
+ size_t argument_name_length = 0;
+ const char* current_argument = NULL;
+ int retval = -1;
+
+ optarg = NULL;
+ optopt = 0;
+
+ if (optind >= argc)
+ return -1;
+
+ if (strlen(argv[optind]) < 3 || strncmp(argv[optind], "--", 2) != 0)
+ return getopt(argc, argv, optstring);
+
+ /* It's an option; starts with -- and is longer than two chars. */
+ current_argument = argv[optind] + 2;
+ argument_name_length = strcspn(current_argument, "=");
+ for (; o->name; ++o) {
+ if (strncmp(o->name, current_argument, argument_name_length) == 0) {
+ match = o;
+ ++num_matches;
+ }
+ }
+
+ if (num_matches == 1) {
+ /* If longindex is not NULL, it points to a variable which is set to the
+ index of the long option relative to longopts. */
+ if (longindex)
+ *longindex = (match - longopts);
+
+ /* If flag is NULL, then getopt_long() shall return val.
+ Otherwise, getopt_long() returns 0, and flag shall point to a variable
+ which shall be set to val if the option is found, but left unchanged if
+ the option is not found. */
+ if (match->flag)
+ *(match->flag) = match->val;
+
+ retval = match->flag ? 0 : match->val;
+
+ if (match->has_arg != no_argument) {
+ optarg = strchr(argv[optind], '=');
+ if (optarg != NULL)
+ ++optarg;
+
+ if (match->has_arg == required_argument) {
+ /* Only scan the next argv for required arguments. Behavior is not
+ specified, but has been observed with Ubuntu and Mac OSX. */
+ if (optarg == NULL && ++optind < argc) {
+ optarg = argv[optind];
+ }
+
+ if (optarg == NULL)
+ retval = ':';
+ }
+ } else if (strchr(argv[optind], '=')) {
+ /* An argument was provided to a non-argument option.
+ I haven't seen this specified explicitly, but both GNU and BSD-based
+ implementations show this behavior.
+ */
+ retval = '?';
+ }
+ } else {
+ /* Unknown option or ambiguous match. */
+ retval = '?';
+ }
+
+ ++optind;
+ return retval;
+}
diff --git a/capture/src/getopt.h b/capture/src/getopt.h
new file mode 100644
index 00000000..a1d6db03
--- /dev/null
+++ b/capture/src/getopt.h
@@ -0,0 +1,59 @@
+/*******************************************************************************
+ * Copyright (c) 2012-2017, Kim Grasman
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * 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.
+ * * Neither the name of Kim Grasman nor the
+ * names of contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * 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 KIM GRASMAN 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.
+ *
+ ******************************************************************************/
+
+#ifndef INCLUDED_GETOPT_PORT_H
+#define INCLUDED_GETOPT_PORT_H
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+#define no_argument 1
+#define required_argument 2
+#define optional_argument 3
+
+extern char* optarg;
+extern int optind, opterr, optopt;
+
+struct option {
+ const char* name;
+ int has_arg;
+ int* flag;
+ int val;
+};
+
+int getopt(int argc, char* const argv[], const char* optstring);
+
+int getopt_long(int argc, char* const argv[],
+ const char* optstring, const struct option* longopts, int* longindex);
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif // INCLUDED_GETOPT_PORT_H