about summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorptitSeb <sebastien.chev@gmail.com>2021-02-28 14:19:04 +0100
committerptitSeb <sebastien.chev@gmail.com>2021-02-28 14:19:04 +0100
commite753c19da1b621b1e667de85ce5ef60d186f0684 (patch)
tree9bc80378efbe42ec72678230de2905ab121a4d8f
parent7b50468b61b7bdd9a15753c0a28711c1654a8c12 (diff)
downloadbox64-e753c19da1b621b1e667de85ce5ef60d186f0684.tar.gz
box64-e753c19da1b621b1e667de85ce5ef60d186f0684.zip
Added some minimal set of source (now box64 compile and say hello at least)
-rwxr-xr-xCMakeLists.txt273
-rw-r--r--cmake_uninstall.cmake.in21
-rwxr-xr-xrunTest.cmake50
-rwxr-xr-xsrc/box64context.c200
-rwxr-xr-xsrc/box64version.h8
-rwxr-xr-xsrc/build_info.c27
-rwxr-xr-xsrc/build_info.h6
-rwxr-xr-xsrc/include/box64context.h74
-rwxr-xr-xsrc/include/debug.h42
-rwxr-xr-xsrc/include/fileutils.h23
-rwxr-xr-xsrc/include/khash.h678
-rwxr-xr-xsrc/include/pathcoll.h23
-rwxr-xr-xsrc/include/wine_tools.h13
-rwxr-xr-xsrc/main.c734
-rwxr-xr-xsrc/tools/fileutils.c129
-rwxr-xr-xsrc/tools/pathcoll.c182
-rwxr-xr-xsrc/tools/wine_tools.c107
17 files changed, 2590 insertions, 0 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
new file mode 100755
index 00000000..42b9a942
--- /dev/null
+++ b/CMakeLists.txt
@@ -0,0 +1,273 @@
+cmake_minimum_required(VERSION 3.0)
+
+option(RPI4ARM64 "Set to ON if targeting an RaspberryPI4 device with multiarch arm64 and armhf" ${RPI4ARM64})
+option(RK3399 "Set to ON if targeting an Rockchip RK3399 based device" ${RK3399})
+option(USE_CCACHE "Set to ON to use ccache if present in the system" ${USE_CCACHE})
+option(HAVE_TRACE "Set to ON to have Trace ability (needs ZydisInfo library)" ${HAVE_TRACE})
+option(NOGIT "Set to ON if not building from a git clone repo (like when building from a zip download from github)" ${NOGIT})
+option(LD80BITS "Set to ON if host device have 80bits long double (i.e. i386)" ${LD80BITS})
+option(NOALIGN "Set to ON if host device doesn't need re-align (i.e. i386)" ${NOALIGN})
+
+if(${CMAKE_VERSION} VERSION_LESS "3.12.2")
+    find_package(PythonInterp 3)
+    if(NOT PYTHONINTERP_FOUND)
+        message( FATAL_ERROR "You need a Python interpretor, CMake will exit." )
+    endif()
+    if(${PYTHON_VERSION_MAJOR} LESS 3)
+        message( FATAL_ERROR "You need a Python 3 interpretor, CMake will exit." )
+    endif()
+else()
+    find_package(Python3)
+    if(NOT Python3_Interpreter_FOUND)
+        message( FATAL_ERROR "You need a Python interpretor, CMake will exit." )
+    endif()
+    set(PYTHON_EXECUTABLE ${Python3_EXECUTABLE} CACHE INTERNAL "The Python3 executable" FORCE)
+endif()
+
+project(box64 C ASM)
+
+enable_testing()
+
+set(default_build_type "RelwithDebInfo")
+if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
+  message(STATUS "Setting build type to '${default_build_type}' as none was specified.")
+  set(CMAKE_BUILD_TYPE "${default_build_type}" CACHE STRING "Choose the type of build." FORCE)
+  set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug" "Release" "MinSizeRel" "RelWithDebInfo")
+endif()
+
+if(RPI4ARM64)
+    add_definitions(-DRPI)
+    add_definitions(-DRPI4ARM64)
+    add_definitions(-pipe -march=armv8-a+crc -mtune=cortex-a72 -mfpu=neon-fp-armv8)
+    set(CMAKE_ASM_FLAGS  "-pipe -march=armv8-a+crc -mtune=cortex-a72 -mfpu=neon-fp-armv8")
+endif()
+if(RK3399)
+    add_definitions(-DRK3399)
+    add_definitions(-pipe -march=armv8-a+crc+simd+crypto -mcpu=cortex-a72+crypto)
+    set(CMAKE_ASM_FLAGS  "-pipe -march=armv8-a+crc+simd+crypto -mcpu=cortex-a72+crypto")
+endif()
+
+if(NOGIT)
+    add_definitions(-DNOGIT)
+endif()
+
+if(HAVE_TRACE)
+    set(BOX64 box64t)
+else()
+    set(BOX64 box64)
+endif()
+
+set(BOX64_ELF_ADDRESS "0x50062800000")  #random load address...
+
+if(LD80BITS)
+    add_definitions(-DHAVE_LD80BITS)
+endif()
+
+if(NOALIGN)
+    add_definitions(-DNOALIGN)
+endif()
+
+if(HAVE_TRACE)
+    add_definitions(-DHAVE_TRACE)
+endif()
+
+set(BOX64_ROOT ${CMAKE_SOURCE_DIR})
+
+add_definitions(-std=gnu11 -funwind-tables -fvisibility=hidden)
+
+if(USE_CCACHE)
+    find_program(CCACHE_FOUND ccache)
+    if(CCACHE_FOUND)
+        set_property(GLOBAL PROPERTY RULE_LAUNCH_COMPILE ccache)
+        set_property(GLOBAL PROPERTY RULE_LAUNCH_LINK ccache)
+    endif()
+endif()
+
+include_directories(
+    "${BOX64_ROOT}/src/include"
+    "${BOX64_ROOT}/src"
+    "${BOX64_ROOT}/src/wrapped/generated"
+)
+
+# git_head.h is a generated file
+set_source_files_properties(
+    "${BOX64_ROOT}/src/git_head.h"
+    PROPERTIES GENERATED TRUE
+    HEADER_FILE_ONLY TRUE)
+
+set(ELFLOADER_SRC
+    "${BOX64_ROOT}/src/main.c"
+    "${BOX64_ROOT}/src/box64context.c"
+    "${BOX64_ROOT}/src/build_info.c"
+    "${BOX64_ROOT}/src/tools/pathcoll.c"
+    "${BOX64_ROOT}/src/tools/fileutils.c"
+    "${BOX64_ROOT}/src/tools/wine_tools.c"
+)
+
+#set(WRAPPEDS
+#    "${BOX64_ROOT}/src/wrapped/wrappedlibc.c"
+#)
+
+#set(WRAPPEDS_HEAD "${BOX64_ROOT}/src/wrapped/wrappedd3dadapter9_gen.h")
+#foreach(A ${WRAPPEDS})
+#    string(REPLACE ".c" "_private.h" B ${A})
+#    set(WRAPPEDS_HEAD ${WRAPPEDS_HEAD} ${B})
+#    set_source_files_properties(${A} PROPERTIES OBJECT_DEPENDS ${B})
+#endforeach()
+
+#set(WRAPPER "${BOX64_ROOT}/src/wrapped/generated/wrapper.c" "${BOX64_ROOT}/src/wrapped/generated/wrapper.h")
+
+#add_custom_command(
+#    OUTPUT "${BOX64_ROOT}/src/wrapped/generated/functions_list.txt"
+#    COMMAND "${PYTHON_EXECUTABLE}" "${BOX64_ROOT}/rebuild_wrappers.py" 
+#    "${BOX64_ROOT}" 
+#    "PANDORA" "HAVE_LD80BITS" "NOALIGN" "HAVE_TRACE" "POWERPCLE" "--" 
+#    ${WRAPPEDS_HEAD}
+#    MAIN_DEPENDENCY "${BOX64_ROOT}/rebuild_wrappers.py"
+#    DEPENDS ${WRAPPEDS} ${WRAPPEDS_HEAD}
+#    BYPRODUCTS ${WRAPPER}
+#)
+
+#add_custom_command(
+#    OUTPUT "${BOX64_ROOT}/src/dynarec/last_run.txt"
+#    COMMAND "${PYTHON_EXECUTABLE}" "${BOX64_ROOT}/rebuild_printer.py" "${BOX64_ROOT}"
+#    MAIN_DEPENDENCY "${BOX64_ROOT}/rebuild_printer.py"
+#    DEPENDS "${BOX64_ROOT}/src/dynarec/arm_instructions.txt"
+#    BYPRODUCTS "${BOX64_ROOT}/src/dynarec/arm_printer.c"
+#)
+
+#add_custom_target(WRAPPERS DEPENDS "${BOX64_ROOT}/src/wrapped/generated/functions_list.txt")
+#add_custom_target(PRINTER DEPENDS "${BOX64_ROOT}/src/dynarec/last_run.txt")
+
+# creates git_head.h
+#if(ARM_DYNAREC)
+#    add_custom_command(
+#        OUTPUT "${BOX64_ROOT}/src/git_head.h"
+#        COMMAND bash -c "echo \\\#define GITREV \\\"$(git rev-parse --short HEAD)\\\">\"${BOX64_ROOT}/src/git_head.h\""
+#        DEPENDS dynarec ${ELFLOADER_SRC} ${WRAPPEDS}
+#        VERBATIM)
+#else()
+    add_custom_command(
+        OUTPUT "${BOX64_ROOT}/src/git_head.h"
+        COMMAND bash -c "echo \\\#define GITREV \\\"$(git rev-parse --short HEAD)\\\">\"${BOX64_ROOT}/src/git_head.h\""
+        DEPENDS ${ELFLOADER_SRC} ${WRAPPEDS}
+        VERBATIM)
+#endif()
+
+add_executable(${BOX64} ${ELFLOADER_SRC} "${BOX64_ROOT}/src/git_head.h")
+#add_executable(${BOX64} ${ELFLOADER_SRC} ${WRAPPEDS} "${BOX64_ROOT}/src/git_head.h")
+#add_dependencies(${BOX64} WRAPPERS)
+#add_dependencies(${BOX64} PRINTER)
+target_link_libraries(${BOX64} m dl rt pthread)
+if(ARM_DYNAREC)
+    target_link_libraries(${BOX64} dynarec)
+endif()
+
+if(${CMAKE_VERSION} VERSION_LESS "3.13")
+    set_target_properties(${BOX64} PROPERTIES LINK_FLAGS "-rdynamic -Wl,-Ttext-segment,${BOX64_ELF_ADDRESS}")
+else()
+    target_link_options(${BOX64} PUBLIC -rdynamic)
+    target_link_options(${BOX64} PUBLIC -Wl,-Ttext-segment,${BOX64_ELF_ADDRESS})
+endif()
+
+string(COMPARE EQUAL "${CMAKE_HOST_SYSTEM_PROCESSOR}" "i686"  _x86)
+string(COMPARE EQUAL "${CMAKE_HOST_SYSTEM_PROCESSOR}" "x86_64"  _x86_64)
+
+if(NOT _x86 AND NOT _x86_64)
+  install(TARGETS ${BOX64}
+    RUNTIME DESTINATION bin)
+  #configure_file(system/box64.conf.cmake system/box64.conf)
+  #install(FILES ${CMAKE_BINARY_DIR}/system/box64.conf DESTINATION /etc/binfmt.d/)
+  install(FILES ${CMAKE_SOURCE_DIR}/x64lib/libstdc++.so.6 DESTINATION /usr/lib/i386-linux-gnu/)
+  install(FILES ${CMAKE_SOURCE_DIR}/x64lib/libstdc++.so.5 DESTINATION /usr/lib/i386-linux-gnu/)
+  install(FILES ${CMAKE_SOURCE_DIR}/x64lib/libgcc_s.so.1 DESTINATION /usr/lib/i386-linux-gnu/)
+endif()
+
+if(NOT TARGET uninstall)
+    configure_file(
+        "${CMAKE_CURRENT_SOURCE_DIR}/cmake_uninstall.cmake.in"
+        "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake"
+        IMMEDIATE @ONLY)
+
+    add_custom_target(uninstall
+        COMMAND ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake)
+endif()
+
+add_test(test01 ${CMAKE_COMMAND} -D TEST_PROGRAM=${CMAKE_BINARY_DIR}/${BOX64} 
+    -D TEST_ARGS=${CMAKE_SOURCE_DIR}/tests/test01 -D TEST_OUTPUT=tmpfile.txt 
+    -D TEST_REFERENCE=${CMAKE_SOURCE_DIR}/tests/ref01.txt
+    -P ${CMAKE_SOURCE_DIR}/runTest.cmake )
+
+add_test(test02 ${CMAKE_COMMAND} -D TEST_PROGRAM=${CMAKE_BINARY_DIR}/${BOX64} 
+    -D TEST_ARGS=${CMAKE_SOURCE_DIR}/tests/test02 -D TEST_OUTPUT=tmpfile.txt 
+    -D TEST_REFERENCE=${CMAKE_SOURCE_DIR}/tests/ref02.txt
+    -P ${CMAKE_SOURCE_DIR}/runTest.cmake )
+
+add_test(test03 ${CMAKE_COMMAND} -D TEST_PROGRAM=${CMAKE_BINARY_DIR}/${BOX64} 
+    -D TEST_ARGS=${CMAKE_SOURCE_DIR}/tests/test03 -D TEST_OUTPUT=tmpfile.txt 
+    -D TEST_REFERENCE=${CMAKE_SOURCE_DIR}/tests/ref03.txt
+    -P ${CMAKE_SOURCE_DIR}/runTest.cmake )
+
+add_test(test04 ${CMAKE_COMMAND} -D TEST_PROGRAM=${CMAKE_BINARY_DIR}/${BOX64} 
+    -D TEST_ARGS=${CMAKE_SOURCE_DIR}/tests/test04 -D TEST_ARGS2=yeah -D TEST_OUTPUT=tmpfile.txt 
+    -D TEST_REFERENCE=${CMAKE_SOURCE_DIR}/tests/ref04.txt
+    -P ${CMAKE_SOURCE_DIR}/runTest.cmake )
+
+add_test(test05 ${CMAKE_COMMAND} -D TEST_PROGRAM=${CMAKE_BINARY_DIR}/${BOX64} 
+    -D TEST_ARGS=${CMAKE_SOURCE_DIR}/tests/test05 -D TEST_ARGS2=7 -D TEST_OUTPUT=tmpfile.txt 
+    -D TEST_REFERENCE=${CMAKE_SOURCE_DIR}/tests/ref05.txt
+    -P ${CMAKE_SOURCE_DIR}/runTest.cmake )
+
+add_test(test06 ${CMAKE_COMMAND} -D TEST_PROGRAM=${CMAKE_BINARY_DIR}/${BOX64} 
+    -D TEST_ARGS=${CMAKE_SOURCE_DIR}/tests/test06 -D TEST_OUTPUT=tmpfile.txt 
+    -D TEST_REFERENCE=${CMAKE_SOURCE_DIR}/tests/ref06.txt
+    -P ${CMAKE_SOURCE_DIR}/runTest.cmake )
+
+add_test(test07 ${CMAKE_COMMAND} -D TEST_PROGRAM=${CMAKE_BINARY_DIR}/${BOX64} 
+    -D TEST_ARGS=${CMAKE_SOURCE_DIR}/tests/test07 -D TEST_OUTPUT=tmpfile.txt 
+    -D TEST_REFERENCE=${CMAKE_SOURCE_DIR}/tests/ref07.txt
+    -P ${CMAKE_SOURCE_DIR}/runTest.cmake )
+
+add_test(test08 ${CMAKE_COMMAND} -D TEST_PROGRAM=${CMAKE_BINARY_DIR}/${BOX64} 
+    -D TEST_ARGS=${CMAKE_SOURCE_DIR}/tests/test08 -D TEST_OUTPUT=tmpfile.txt 
+    -D TEST_REFERENCE=${CMAKE_SOURCE_DIR}/tests/ref08.txt
+    -P ${CMAKE_SOURCE_DIR}/runTest.cmake )
+
+add_test(test09 ${CMAKE_COMMAND} -D TEST_PROGRAM=${CMAKE_BINARY_DIR}/${BOX64} 
+    -D TEST_ARGS=${CMAKE_SOURCE_DIR}/tests/test09 -D TEST_OUTPUT=tmpfile.txt 
+    -D TEST_REFERENCE=${CMAKE_SOURCE_DIR}/tests/ref09.txt
+    -P ${CMAKE_SOURCE_DIR}/runTest.cmake )
+
+add_test(test10 ${CMAKE_COMMAND} -D TEST_PROGRAM=${CMAKE_BINARY_DIR}/${BOX64} 
+    -D TEST_ARGS=${CMAKE_SOURCE_DIR}/tests/test10 -D TEST_OUTPUT=tmpfile.txt 
+    -D TEST_REFERENCE=${CMAKE_SOURCE_DIR}/tests/ref10.txt
+    -P ${CMAKE_SOURCE_DIR}/runTest.cmake )
+
+add_test(test11 ${CMAKE_COMMAND} -D TEST_PROGRAM=${CMAKE_BINARY_DIR}/${BOX64} 
+    -D TEST_ARGS=${CMAKE_SOURCE_DIR}/tests/test11 -D TEST_OUTPUT=tmpfile.txt 
+    -D TEST_REFERENCE=${CMAKE_SOURCE_DIR}/tests/ref11.txt
+    -P ${CMAKE_SOURCE_DIR}/runTest.cmake )
+
+add_test(test12 ${CMAKE_COMMAND} -D TEST_PROGRAM=${CMAKE_BINARY_DIR}/${BOX64} 
+    -D TEST_ARGS=${CMAKE_SOURCE_DIR}/tests/test12 -D TEST_OUTPUT=tmpfile.txt 
+    -D TEST_REFERENCE=${CMAKE_SOURCE_DIR}/tests/ref12.txt
+    -P ${CMAKE_SOURCE_DIR}/runTest.cmake )
+
+add_test(test13 ${CMAKE_COMMAND} -D TEST_PROGRAM=${CMAKE_BINARY_DIR}/${BOX64} 
+    -D TEST_ARGS=${CMAKE_SOURCE_DIR}/tests/test13 -D TEST_OUTPUT=tmpfile.txt 
+    -D TEST_REFERENCE=${CMAKE_SOURCE_DIR}/tests/ref13.txt
+    -P ${CMAKE_SOURCE_DIR}/runTest.cmake )
+
+add_test(test14 ${CMAKE_COMMAND} -D TEST_PROGRAM=${CMAKE_BINARY_DIR}/${BOX64} 
+    -D TEST_ARGS=${CMAKE_SOURCE_DIR}/tests/test14 -D TEST_OUTPUT=tmpfile.txt 
+    -D TEST_REFERENCE=${CMAKE_SOURCE_DIR}/tests/ref14.txt
+    -P ${CMAKE_SOURCE_DIR}/runTest.cmake )
+
+file(GLOB extension_tests "${CMAKE_SOURCE_DIR}/tests/extensions/*.c")
+foreach(file ${extension_tests})
+    get_filename_component(testname "${file}" NAME_WE)
+    add_test(NAME "${testname}" COMMAND ${CMAKE_COMMAND} -D TEST_PROGRAM=${CMAKE_BINARY_DIR}/${BOX64}
+        -D TEST_ARGS=${CMAKE_SOURCE_DIR}/tests/extensions/${testname} -D TEST_OUTPUT=tmpfile.txt
+        -D TEST_REFERENCE=${CMAKE_SOURCE_DIR}/tests/extensions/${testname}.txt
+        -P ${CMAKE_SOURCE_DIR}/runTest.cmake)
+endforeach()
diff --git a/cmake_uninstall.cmake.in b/cmake_uninstall.cmake.in
new file mode 100644
index 00000000..4c07dc7b
--- /dev/null
+++ b/cmake_uninstall.cmake.in
@@ -0,0 +1,21 @@
+if(NOT EXISTS "@CMAKE_BINARY_DIR@/install_manifest.txt")
+  message(FATAL_ERROR "Cannot find install manifest: @CMAKE_BINARY_DIR@/install_manifest.txt")
+endif(NOT EXISTS "@CMAKE_BINARY_DIR@/install_manifest.txt")
+
+file(READ "@CMAKE_BINARY_DIR@/install_manifest.txt" files)
+string(REGEX REPLACE "\n" ";" files "${files}")
+foreach(file ${files})
+  message(STATUS "Uninstalling $ENV{DESTDIR}${file}")
+  if(IS_SYMLINK "$ENV{DESTDIR}${file}" OR EXISTS "$ENV{DESTDIR}${file}")
+    exec_program(
+      "@CMAKE_COMMAND@" ARGS "-E remove \"$ENV{DESTDIR}${file}\""
+      OUTPUT_VARIABLE rm_out
+      RETURN_VALUE rm_retval
+      )
+    if(NOT "${rm_retval}" STREQUAL 0)
+      message(FATAL_ERROR "Problem when removing $ENV{DESTDIR}${file}")
+    endif(NOT "${rm_retval}" STREQUAL 0)
+  else(IS_SYMLINK "$ENV{DESTDIR}${file}" OR EXISTS "$ENV{DESTDIR}${file}")
+    message(STATUS "File $ENV{DESTDIR}${file} does not exist.")
+  endif(IS_SYMLINK "$ENV{DESTDIR}${file}" OR EXISTS "$ENV{DESTDIR}${file}")
+endforeach(file)
diff --git a/runTest.cmake b/runTest.cmake
new file mode 100755
index 00000000..cb6706ae
--- /dev/null
+++ b/runTest.cmake
@@ -0,0 +1,50 @@
+# arguments checking
+if( NOT TEST_PROGRAM )
+  message( FATAL_ERROR "Require TEST_PROGRAM to be defined" )
+endif( NOT TEST_PROGRAM )
+if( NOT TEST_ARGS )
+  message( FATAL_ERROR "Require TEST_ARGS to be defined" )
+endif( NOT TEST_ARGS )
+if( NOT TEST_OUTPUT )
+  message( FATAL_ERROR "Require TEST_OUTPUT to be defined" )
+endif( NOT TEST_OUTPUT )
+if( NOT TEST_REFERENCE )
+  message( FATAL_ERROR "Require TEST_REFERENCE to be defined" )
+endif( NOT TEST_REFERENCE )
+
+set(ENV{BOX64_LOG} 0)
+set(ENV{BOX64_NOBANNER} 1)
+if( EXISTS ${CMAKE_SOURCE_DIR}/x64lib )
+  # we are inside box64 folder
+  set(ENV{LD_LIBRARY_PATH} ${CMAKE_SOURCE_DIR}/x64lib)
+else()
+  # we are inside build folder
+  set(ENV{LD_LIBRARY_PATH} ${CMAKE_SOURCE_DIR}/../x64lib)
+endif( EXISTS ${CMAKE_SOURCE_DIR}/x64lib )
+
+# run the test program, capture the stdout/stderr and the result var
+execute_process(
+  COMMAND ${TEST_PROGRAM} ${TEST_ARGS} ${TEST_ARGS2}
+  OUTPUT_FILE ${TEST_OUTPUT}
+  ERROR_VARIABLE TEST_ERROR
+  RESULT_VARIABLE TEST_RESULT
+  )
+
+# if the return value is !=0 bail out
+if( TEST_RESULT )
+  message( FATAL_ERROR "Failed: Test program ${TEST_PROGRAM} exited != 0.\n${TEST_ERROR}" )
+endif( TEST_RESULT )
+
+# now compare the output with the reference
+execute_process(
+  COMMAND ${CMAKE_COMMAND} -E compare_files ${TEST_OUTPUT} ${TEST_REFERENCE}
+  RESULT_VARIABLE TEST_RESULT
+  )
+
+# again, if return value is !=0 scream and shout
+if( TEST_RESULT )
+  message( FATAL_ERROR "Failed: The output of ${TEST_PROGRAM} did not match ${TEST_REFERENCE}")
+endif( TEST_RESULT )
+
+# everything went fine...
+message( "Passed: The output of ${TEST_PROGRAM} matches ${TEST_REFERENCE}" )
diff --git a/src/box64context.c b/src/box64context.c
new file mode 100755
index 00000000..21768fb2
--- /dev/null
+++ b/src/box64context.c
@@ -0,0 +1,200 @@
+#define _GNU_SOURCE         /* See feature_test_macros(7) */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <dlfcn.h>
+#include <signal.h>
+#include <sys/mman.h>
+
+#include "box64context.h"
+#include "debug.h"
+
+
+EXPORTDYN
+void initAllHelpers(box64context_t* context)
+{
+    static int inited = 0;
+    if(inited)
+        return;
+    my_context = context;
+    //init_pthread_helper();
+    //init_signal_helper(context);
+    inited = 1;
+}
+
+EXPORTDYN
+void finiAllHelpers(box64context_t* context)
+{
+    static int finied = 0;
+    if(finied)
+        return;
+    //fini_pthread_helper(context);
+    //fini_signal_helper();
+    //cleanAlternate();
+    //fini_custommem_helper(context);
+    finied = 1;
+}
+
+/// maxval not inclusive
+int getrand(int maxval)
+{
+    if(maxval<1024) {
+        return ((random()&0x7fff)*maxval)/0x7fff;
+    } 
+        uint64_t r = random();
+        r = (r*maxval) / RAND_MAX;
+        return r;
+
+}
+
+void free_tlsdatasize(void* p)
+{
+    if(!p)
+        return;
+    tlsdatasize_t *data = (tlsdatasize_t*)p;
+    free(data->tlsdata);
+    free(p);
+}
+
+EXPORTDYN
+box64context_t *NewBox64Context(int argc)
+{
+#ifdef BUILD_DYNAMIC
+    if(my_context) {
+        ++my_context->count;
+        return my_context;
+    }
+#endif
+    // init and put default values
+    box64context_t *context = my_context = (box64context_t*)calloc(1, sizeof(box64context_t));
+
+    context->deferedInit = 1;
+    context->sel_serial = 1;
+
+    //init_custommem_helper(context);
+
+    context->box64lib = dlopen(NULL, RTLD_NOW|RTLD_GLOBAL);
+    //context->dlprivate = NewDLPrivate();
+
+    context->argc = argc;
+    context->argv = (char**)calloc(context->argc+1, sizeof(char*));
+
+    for (int i=0; i<4; ++i) context->canary[i] = 1 +  getrand(255);
+    context->canary[getrand(4)] = 0;
+    printf_log(LOG_DEBUG, "Setting up canary (for Stack protector) at GS:0x14, value:%08X\n", *(uint32_t*)context->canary);
+
+    initAllHelpers(context);
+
+    return context;
+}
+
+EXPORTDYN
+void FreeBox64Context(box64context_t** context)
+{
+    if(!context)
+        return;
+    
+    box64context_t* ctx = *context;   // local copy to do the cleanning
+
+    FreeCollection(&ctx->box64_path);
+    FreeCollection(&ctx->box64_ld_lib);
+    FreeCollection(&ctx->box64_emulated_libs);
+    // stop trace now
+    /*if(ctx->dec)
+        DeleteX86TraceDecoder(&ctx->dec);
+    if(ctx->zydis)
+        DeleteX86Trace(ctx);*/
+
+    free(ctx->argv);
+    
+    for (int i=0; i<ctx->envc; ++i)
+        free(ctx->envv[i]);
+    free(ctx->envv);
+
+    for(int i=0; i<MAX_SIGNAL; ++i)
+        if(ctx->signals[i]!=0 && ctx->signals[i]!=1) {
+            signal(i, SIG_DFL);
+        }
+
+    *context = NULL;                // bye bye my_context
+
+    //CleanStackSize(ctx);
+
+#ifndef BUILD_LIB
+    if(ctx->box64lib)
+        dlclose(ctx->box64lib);
+#endif
+
+    //FreeDLPrivate(&ctx->dlprivate);
+
+    free(ctx->stack);
+
+    free(ctx->fullpath);
+    free(ctx->box64path);
+
+    void* ptr;
+    if ((ptr = pthread_getspecific(ctx->tlskey)) != NULL) {
+        free_tlsdatasize(ptr);
+        pthread_setspecific(ctx->tlskey, NULL);
+    }
+    pthread_key_delete(ctx->tlskey);
+
+    if(ctx->tlsdata)
+        free(ctx->tlsdata);
+
+    finiAllHelpers(ctx);
+
+    free(ctx);
+}
+/*
+int AddElfHeader(box64context_t* ctx, elfheader_t* head) {
+    int idx = ctx->elfsize;
+    if(idx==ctx->elfcap) {
+        // resize...
+        ctx->elfcap += 16;
+        ctx->elfs = (elfheader_t**)realloc(ctx->elfs, sizeof(elfheader_t*) * ctx->elfcap);
+    }
+    ctx->elfs[idx] = head;
+    ctx->elfsize++;
+    printf_log(LOG_DEBUG, "Adding \"%s\" as #%d in elf collection\n", ElfName(head), idx);
+    return idx;
+}
+*/
+int AddTLSPartition(box64context_t* context, int tlssize) {
+    int oldsize = context->tlssize;
+    context->tlssize += tlssize;
+    context->tlsdata = realloc(context->tlsdata, context->tlssize);
+    memmove(context->tlsdata+tlssize, context->tlsdata, oldsize);   // move to the top, using memmove as regions will probably overlap
+    memset(context->tlsdata, 0, tlssize);           // fill new space with 0 (not mandatory)
+    // clean GS segment for current emu
+    if(my_context) {
+        //ResetSegmentsCache(thread_get_emu());
+        if(!(++context->sel_serial))
+            ++context->sel_serial;
+    }
+
+    return -context->tlssize;   // negative offset
+}
+/*
+void add_neededlib(needed_libs_t* needed, library_t* lib)
+{
+    if(!needed)
+        return;
+    if(needed->size == needed->cap) {
+        needed->cap += 8;
+        needed->libs = (library_t**)realloc(needed->libs, needed->cap*sizeof(library_t*));
+    }
+    needed->libs[needed->size++] = lib;
+}
+
+void free_neededlib(needed_libs_t* needed)
+{
+    if(!needed)
+        return;
+    needed->cap = 0;
+    needed->size = 0;
+    if(needed->libs)
+        free(needed->libs);
+    needed->libs = NULL;
+}
+*/
\ No newline at end of file
diff --git a/src/box64version.h b/src/box64version.h
new file mode 100755
index 00000000..d9c4c042
--- /dev/null
+++ b/src/box64version.h
@@ -0,0 +1,8 @@
+#ifndef __BOX64_VERSION_H_
+#define __BOX64_VERSION_H_
+
+#define BOX64_MAJOR 0
+#define BOX64_MINOR 0
+#define BOX64_REVISION 1
+
+#endif //__BOX64_VERSION_H_
\ No newline at end of file
diff --git a/src/build_info.c b/src/build_info.c
new file mode 100755
index 00000000..02c19b4f
--- /dev/null
+++ b/src/build_info.c
@@ -0,0 +1,27 @@
+#include <stdio.h>
+#include "debug.h"
+#include "box64version.h"
+#ifdef NOGIT
+#define GITREV "nogit"
+#else
+#include "git_head.h"
+#endif
+
+void PrintBox64Version()
+{
+    printf("Box64%s%s v%d.%d.%d %s built on %s %s\n", 
+    #ifdef HAVE_TRACE
+        " with trace",
+    #else
+        "",
+    #endif
+    #ifdef DYNAREC
+        " with Dynarec",
+    #else
+        "",
+    #endif
+        BOX64_MAJOR, BOX64_MINOR, BOX64_REVISION,
+        GITREV,
+        __DATE__, __TIME__);
+}
+
diff --git a/src/build_info.h b/src/build_info.h
new file mode 100755
index 00000000..45182a02
--- /dev/null
+++ b/src/build_info.h
@@ -0,0 +1,6 @@
+#ifndef __BUILD_INFO_H__
+#define __BUILD_INFO_H__
+
+void PrintBox64Version();
+
+#endif //__BUILD_INFO_H__
\ No newline at end of file
diff --git a/src/include/box64context.h b/src/include/box64context.h
new file mode 100755
index 00000000..680b2d5e
--- /dev/null
+++ b/src/include/box64context.h
@@ -0,0 +1,74 @@
+#ifndef __BOX64CONTEXT_H_
+#define __BOX64CONTEXT_H_
+#include <stdint.h>
+#include <pthread.h>
+#include "pathcoll.h"
+
+typedef void* (*procaddess_t)(const char* name);
+typedef void* (*vkprocaddess_t)(void* instance, const char* name);
+
+#define MAX_SIGNAL 64
+
+typedef struct tlsdatasize_s {
+    int32_t     tlssize;
+    void*       tlsdata;
+} tlsdatasize_t;
+
+void free_tlsdatasize(void* p);
+
+typedef struct box64context_s {
+    path_collection_t   box64_path;     // PATH env. variable
+    path_collection_t   box64_ld_lib;   // LD_LIBRARY_PATH env. variable
+
+    path_collection_t   box64_emulated_libs;    // Collection of libs that should not be wrapped
+
+    int                 x86trace;
+    int                 trace_tid;
+
+    uint32_t            sel_serial;     // will be increment each time selectors changes
+
+    //zydis_t             *zydis;         // dlopen the zydis dissasembler
+    void*               box64lib;       // dlopen on box86 itself
+
+    int                 argc;
+    char**              argv;
+
+    int                 envc;
+    char**              envv;
+
+    char*               fullpath;
+    char*               box64path;      // path of current box86 executable
+
+    uint32_t            stacksz;
+    int                 stackalign;
+    void*               stack;          // alocated stack
+
+    int                 deferedInit;
+
+    pthread_key_t       tlskey;     // then tls key to have actual tlsdata
+    void*               tlsdata;    // the initial global tlsdata
+    int32_t             tlssize;    // wanted size of tlsdata
+
+    //zydis_dec_t         *dec;           // trace
+
+    uint8_t             canary[4];
+
+    uintptr_t           signals[MAX_SIGNAL];
+    uintptr_t           restorer[MAX_SIGNAL];
+    int                 onstack[MAX_SIGNAL];
+    int                 is_sigaction[MAX_SIGNAL];
+
+    int                 no_sigsegv;
+    int                 no_sigill;
+
+} box64context_t;
+
+extern box64context_t *my_context; // global context
+
+box64context_t *NewBox64Context(int argc);
+void FreeBox64Context(box64context_t** context);
+
+// return the tlsbase (negative) for the new TLS partition created (no partition index is stored in the context)
+int AddTLSPartition(box64context_t* context, int tlssize);
+
+#endif //__BOX64CONTEXT_H_
\ No newline at end of file
diff --git a/src/include/debug.h b/src/include/debug.h
new file mode 100755
index 00000000..8b440787
--- /dev/null
+++ b/src/include/debug.h
@@ -0,0 +1,42 @@
+#ifndef __DEBUG_H_
+#define __DEBUG_H_
+#include <stdint.h>
+
+typedef struct box64context_s box64context_t;
+extern int box64_log;    // log level
+extern int box64_dynarec_log;
+extern int box64_dynarec;
+extern int box64_pagesize;
+extern int dlsym_error;  // log dlsym error
+extern int trace_xmm;    // include XMM reg in trace?
+extern int trace_emm;    // include EMM reg in trace?
+extern int allow_missing_libs;
+extern int box64_steam;
+extern int box64_nopulse;   // disabling the use of wrapped pulseaudio
+extern int box64_nogtk; // disabling the use of wrapped gtk
+extern int box64_novulkan;  // disabling the use of wrapped vulkan
+extern uintptr_t   trace_start, trace_end;
+extern char* trace_func;
+extern uintptr_t fmod_smc_start, fmod_smc_end; // to handle libfmod (from Unreal) SMC (self modifying code)
+extern uint32_t default_fs;
+extern int jit_gdb; // launch gdb when a segfault is trapped
+extern int box64_tcmalloc_minimal;  // when using tcmalloc_minimal
+#define LOG_NONE 0
+#define LOG_INFO 1
+#define LOG_DEBUG 2
+#define LOG_DUMP 3
+
+extern FILE* ftrace;
+
+#define printf_log(L, ...) do {if(L<=box64_log) {fprintf(ftrace, __VA_ARGS__); fflush(ftrace);}} while(0)
+
+#define dynarec_log(L, ...) do {if(L<=box64_dynarec_log) {fprintf(ftrace, __VA_ARGS__); /*fflush(ftrace);*/}} while(0)
+
+#define EXPORT __attribute__((visibility("default")))
+#ifdef BUILD_DYNAMIC
+#define EXPORTDYN __attribute__((visibility("default")))
+#else
+#define EXPORTDYN 
+#endif
+
+#endif //__DEBUG_H_
diff --git a/src/include/fileutils.h b/src/include/fileutils.h
new file mode 100755
index 00000000..27256909
--- /dev/null
+++ b/src/include/fileutils.h
@@ -0,0 +1,23 @@
+#ifndef __FILEUTILS_H_
+#define __FILEUTILS_H_
+
+#include "pathcoll.h"
+
+#define IS_EXECUTABLE   (1<<0)
+#define IS_FILE         (1<<1)
+       
+
+// 0 : doesn't exist, 1: Does exist
+int FileExist(const char* filename, int flags);
+
+// find a file, using Path if needed
+char* ResolveFile(const char* filename, path_collection_t* paths);
+
+// 1: if file is an x86 elf, 0: if not (or not found)
+int FileIsX64ELF(const char* filename);
+
+#if defined(RPI) || defined(RK3399)
+void sanitize_mojosetup_gtk_background();
+#endif
+
+#endif //__FILEUTILS_H_
\ No newline at end of file
diff --git a/src/include/khash.h b/src/include/khash.h
new file mode 100755
index 00000000..dfce7aff
--- /dev/null
+++ b/src/include/khash.h
@@ -0,0 +1,678 @@
+/* The MIT License
+
+   Copyright (c) 2008, 2009, 2011 by Attractive Chaos <attractor@live.co.uk>
+
+   Permission is hereby granted, free of charge, to any person obtaining
+   a copy of this software and associated documentation files (the
+   "Software"), to deal in the Software without restriction, including
+   without limitation the rights to use, copy, modify, merge, publish,
+   distribute, sublicense, and/or sell copies of the Software, and to
+   permit persons to whom the Software is furnished to do so, subject to
+   the following conditions:
+
+   The above copyright notice and this permission notice shall be
+   included in all copies or substantial portions of the Software.
+
+   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+   EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+   NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+   BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+   ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+   CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+   SOFTWARE.
+*/
+
+/*
+  An example:
+
+#include "khash.h"
+KHASH_MAP_INIT_INT(32, char)
+int main() {
+	int ret, is_missing;
+	khiter_t k;
+	khash_t(32) *h = kh_init(32);
+	k = kh_put(32, h, 5, &ret);
+	kh_value(h, k) = 10;
+	k = kh_get(32, h, 10);
+	is_missing = (k == kh_end(h));
+	k = kh_get(32, h, 5);
+	kh_del(32, h, k);
+	for (k = kh_begin(h); k != kh_end(h); ++k)
+		if (kh_exist(h, k)) kh_value(h, k) = 1;
+	kh_destroy(32, h);
+	return 0;
+}
+*/
+
+/*
+  2013-05-02 (0.2.8):
+
+	* Use quadratic probing. When the capacity is power of 2, stepping function
+	  i*(i+1)/2 guarantees to traverse each bucket. It is better than double
+	  hashing on cache performance and is more robust than linear probing.
+
+	  In theory, double hashing should be more robust than quadratic probing.
+	  However, my implementation is probably not for large hash tables, because
+	  the second hash function is closely tied to the first hash function,
+	  which reduce the effectiveness of double hashing.
+
+	Reference: http://research.cs.vt.edu/AVresearch/hashing/quadratic.php
+
+  2011-12-29 (0.2.7):
+
+    * Minor code clean up; no actual effect.
+
+  2011-09-16 (0.2.6):
+
+	* The capacity is a power of 2. This seems to dramatically improve the
+	  speed for simple keys. Thank Zilong Tan for the suggestion. Reference:
+
+	   - http://code.google.com/p/ulib/
+	   - http://nothings.org/computer/judy/
+
+	* Allow to optionally use linear probing which usually has better
+	  performance for random input. Double hashing is still the default as it
+	  is more robust to certain non-random input.
+
+	* Added Wang's integer hash function (not used by default). This hash
+	  function is more robust to certain non-random input.
+
+  2011-02-14 (0.2.5):
+
+    * Allow to declare global functions.
+
+  2009-09-26 (0.2.4):
+
+    * Improve portability
+
+  2008-09-19 (0.2.3):
+
+	* Corrected the example
+	* Improved interfaces
+
+  2008-09-11 (0.2.2):
+
+	* Improved speed a little in kh_put()
+
+  2008-09-10 (0.2.1):
+
+	* Added kh_clear()
+	* Fixed a compiling error
+
+  2008-09-02 (0.2.0):
+
+	* Changed to token concatenation which increases flexibility.
+
+  2008-08-31 (0.1.2):
+
+	* Fixed a bug in kh_get(), which has not been tested previously.
+
+  2008-08-31 (0.1.1):
+
+	* Added destructor
+*/
+
+
+#ifndef __AC_KHASH_H
+#define __AC_KHASH_H
+
+/*!
+  @header
+
+  Generic hash table library.
+ */
+
+#define AC_VERSION_KHASH_H "0.2.8"
+
+#include <stdlib.h>
+#include <string.h>
+#include <limits.h>
+
+/* compiler specific configuration */
+
+#if UINT_MAX == 0xffffffffu
+typedef unsigned int khint32_t;
+#elif ULONG_MAX == 0xffffffffu
+typedef unsigned long khint32_t;
+#endif
+
+#if ULONG_MAX == ULLONG_MAX
+typedef unsigned long khint64_t;
+#else
+typedef unsigned long long khint64_t;
+#endif
+
+#ifdef _MSC_VER
+#define kh_inline __inline
+#else
+#define kh_inline inline
+#endif
+
+typedef khint32_t khint_t;
+typedef khint_t khiter_t;
+
+#define __ac_isempty(flag, i) ((flag[i>>4]>>((i&0xfU)<<1))&2)
+#define __ac_isdel(flag, i) ((flag[i>>4]>>((i&0xfU)<<1))&1)
+#define __ac_iseither(flag, i) ((flag[i>>4]>>((i&0xfU)<<1))&3)
+#define __ac_set_isdel_false(flag, i) (flag[i>>4]&=~(1ul<<((i&0xfU)<<1)))
+#define __ac_set_isempty_false(flag, i) (flag[i>>4]&=~(2ul<<((i&0xfU)<<1)))
+#define __ac_set_isboth_false(flag, i) (flag[i>>4]&=~(3ul<<((i&0xfU)<<1)))
+#define __ac_set_isdel_true(flag, i) (flag[i>>4]|=1ul<<((i&0xfU)<<1))
+
+#define __ac_fsize(m) ((m) < 16? 1 : (m)>>4)
+
+#ifndef kroundup32
+#define kroundup32(x) (--(x), (x)|=(x)>>1, (x)|=(x)>>2, (x)|=(x)>>4, (x)|=(x)>>8, (x)|=(x)>>16, ++(x))
+#endif
+
+#ifndef kcalloc
+#define kcalloc(N,Z) calloc(N,Z)
+#endif
+#ifndef kmalloc
+#define kmalloc(Z) malloc(Z)
+#endif
+#ifndef krealloc
+#define krealloc(P,Z) realloc(P,Z)
+#endif
+#ifndef kfree
+#define kfree(P) free(P)
+#endif
+
+static const double __ac_HASH_UPPER = 0.77;
+
+#define __KHASH_TYPE(name, khkey_t, khval_t) \
+	typedef struct kh_##name##_s{ \
+		khint_t n_buckets, size, n_occupied, upper_bound; \
+		khint32_t *flags; \
+		khkey_t *keys; \
+		khval_t *vals; \
+	} kh_##name##_t;
+
+#define __KHASH_PROTOTYPES(name, khkey_t, khval_t)	 					\
+	extern kh_##name##_t *kh_init_##name(void);							\
+	extern void kh_destroy_##name(kh_##name##_t *h);					\
+	extern void kh_clear_##name(kh_##name##_t *h);						\
+	extern khint_t kh_get_##name(const kh_##name##_t *h, khkey_t key); 	\
+	extern int kh_resize_##name(kh_##name##_t *h, khint_t new_n_buckets); \
+	extern khint_t kh_put_##name(kh_##name##_t *h, khkey_t key, int *ret); \
+	extern void kh_del_##name(kh_##name##_t *h, khint_t x);
+
+#define __KHASH_IMPL(name, SCOPE, khkey_t, khval_t, kh_is_map, __hash_func, __hash_equal) \
+	SCOPE kh_##name##_t *kh_init_##name(void) {							\
+		return (kh_##name##_t*)kcalloc(1, sizeof(kh_##name##_t));		\
+	}																	\
+	SCOPE void kh_destroy_##name(kh_##name##_t *h)						\
+	{																	\
+		if (h) {														\
+			kfree((void *)h->keys); kfree(h->flags);					\
+			kfree((void *)h->vals);										\
+			kfree(h);													\
+		}																\
+	}																	\
+	SCOPE void kh_clear_##name(kh_##name##_t *h)						\
+	{																	\
+		if (h && h->flags) {											\
+			memset(h->flags, 0xaa, __ac_fsize(h->n_buckets) * sizeof(khint32_t)); \
+			h->size = h->n_occupied = 0;								\
+		}																\
+	}																	\
+	SCOPE khint_t kh_get_##name(const kh_##name##_t *h, khkey_t key) 	\
+	{																	\
+		if (h->n_buckets) {												\
+			khint_t k, i, last, mask, step = 0; \
+			mask = h->n_buckets - 1;									\
+			k = __hash_func(key); i = k & mask;							\
+			last = i; \
+			while (!__ac_isempty(h->flags, i) && (__ac_isdel(h->flags, i) || !__hash_equal(h->keys[i], key))) { \
+				i = (i + (++step)) & mask; \
+				if (i == last) return h->n_buckets;						\
+			}															\
+			return __ac_iseither(h->flags, i)? h->n_buckets : i;		\
+		} else return 0;												\
+	}																	\
+	SCOPE int kh_resize_##name(kh_##name##_t *h, khint_t new_n_buckets) \
+	{ /* This function uses 0.25*n_buckets bytes of working space instead of [sizeof(key_t+val_t)+.25]*n_buckets. */ \
+		khint32_t *new_flags = 0;										\
+		khint_t j = 1;													\
+		{																\
+			kroundup32(new_n_buckets); 									\
+			if (new_n_buckets < 4) new_n_buckets = 4;					\
+			if (h->size >= (khint_t)(new_n_buckets * __ac_HASH_UPPER + 0.5)) j = 0;	/* requested size is too small */ \
+			else { /* hash table size to be changed (shrink or expand); rehash */ \
+				new_flags = (khint32_t*)kmalloc(__ac_fsize(new_n_buckets) * sizeof(khint32_t));	\
+				if (!new_flags) return -1;								\
+				memset(new_flags, 0xaa, __ac_fsize(new_n_buckets) * sizeof(khint32_t)); \
+				if (h->n_buckets < new_n_buckets) {	/* expand */		\
+					khkey_t *new_keys = (khkey_t*)krealloc((void *)h->keys, new_n_buckets * sizeof(khkey_t)); \
+					if (!new_keys) return -1;							\
+					h->keys = new_keys;									\
+					if (kh_is_map) {									\
+						khval_t *new_vals = (khval_t*)krealloc((void *)h->vals, new_n_buckets * sizeof(khval_t)); \
+						if (!new_vals) return -1;						\
+						h->vals = new_vals;								\
+					}													\
+				} /* otherwise shrink */								\
+			}															\
+		}																\
+		if (j) { /* rehashing is needed */								\
+			for (j = 0; j != h->n_buckets; ++j) {						\
+				if (__ac_iseither(h->flags, j) == 0) {					\
+					khkey_t key = h->keys[j];							\
+					khval_t val;										\
+					khint_t new_mask;									\
+					new_mask = new_n_buckets - 1; 						\
+					if (kh_is_map) val = h->vals[j];					\
+					__ac_set_isdel_true(h->flags, j);					\
+					while (1) { /* kick-out process; sort of like in Cuckoo hashing */ \
+						khint_t k, i, step = 0; \
+						k = __hash_func(key);							\
+						i = k & new_mask;								\
+						while (!__ac_isempty(new_flags, i)) i = (i + (++step)) & new_mask; \
+						__ac_set_isempty_false(new_flags, i);			\
+						if (i < h->n_buckets && __ac_iseither(h->flags, i) == 0) { /* kick out the existing element */ \
+							{ khkey_t tmp = h->keys[i]; h->keys[i] = key; key = tmp; } \
+							if (kh_is_map) { khval_t tmp = h->vals[i]; h->vals[i] = val; val = tmp; } \
+							__ac_set_isdel_true(h->flags, i); /* mark it as deleted in the old hash table */ \
+						} else { /* write the element and jump out of the loop */ \
+							h->keys[i] = key;							\
+							if (kh_is_map) h->vals[i] = val;			\
+							break;										\
+						}												\
+					}													\
+				}														\
+			}															\
+			if (h->n_buckets > new_n_buckets) { /* shrink the hash table */ \
+				h->keys = (khkey_t*)krealloc((void *)h->keys, new_n_buckets * sizeof(khkey_t)); \
+				if (kh_is_map) h->vals = (khval_t*)krealloc((void *)h->vals, new_n_buckets * sizeof(khval_t)); \
+			}															\
+			kfree(h->flags); /* free the working space */				\
+			h->flags = new_flags;										\
+			h->n_buckets = new_n_buckets;								\
+			h->n_occupied = h->size;									\
+			h->upper_bound = (khint_t)(h->n_buckets * __ac_HASH_UPPER + 0.5); \
+		}																\
+		return 0;														\
+	}																	\
+	SCOPE khint_t kh_put_##name(kh_##name##_t *h, khkey_t key, int *ret) \
+	{																	\
+		khint_t x;														\
+		if (h->n_occupied >= h->upper_bound) { /* update the hash table */ \
+			if (h->n_buckets > (h->size<<1)) {							\
+				if (kh_resize_##name(h, h->n_buckets - 1) < 0) { /* clear "deleted" elements */ \
+					*ret = -1; return h->n_buckets;						\
+				}														\
+			} else if (kh_resize_##name(h, h->n_buckets + 1) < 0) { /* expand the hash table */ \
+				*ret = -1; return h->n_buckets;							\
+			}															\
+		} /* TODO: to implement automatically shrinking; resize() already support shrinking */ \
+		{																\
+			khint_t k, i, site, last, mask = h->n_buckets - 1, step = 0; \
+			x = site = h->n_buckets; k = __hash_func(key); i = k & mask; \
+			if (__ac_isempty(h->flags, i)) x = i; /* for speed up */	\
+			else {														\
+				last = i; \
+				while (!__ac_isempty(h->flags, i) && (__ac_isdel(h->flags, i) || !__hash_equal(h->keys[i], key))) { \
+					if (__ac_isdel(h->flags, i)) site = i;				\
+					i = (i + (++step)) & mask; \
+					if (i == last) { x = site; break; }					\
+				}														\
+				if (x == h->n_buckets) {								\
+					if (__ac_isempty(h->flags, i) && site != h->n_buckets) x = site; \
+					else x = i;											\
+				}														\
+			}															\
+		}																\
+		if (__ac_isempty(h->flags, x)) { /* not present at all */		\
+			h->keys[x] = key;											\
+			__ac_set_isboth_false(h->flags, x);							\
+			++h->size; ++h->n_occupied;									\
+			*ret = 1;													\
+		} else if (__ac_isdel(h->flags, x)) { /* deleted */				\
+			h->keys[x] = key;											\
+			__ac_set_isboth_false(h->flags, x);							\
+			++h->size;													\
+			*ret = 2;													\
+		} else *ret = 0; /* Don't touch h->keys[x] if present and not deleted */ \
+		return x;														\
+	}																	\
+	SCOPE void kh_del_##name(kh_##name##_t *h, khint_t x)				\
+	{																	\
+		if (x != h->n_buckets && !__ac_iseither(h->flags, x)) {			\
+			__ac_set_isdel_true(h->flags, x);							\
+			--h->size;													\
+		}																\
+	}
+
+#define KHASH_DECLARE(name, khkey_t, khval_t)		 					\
+	__KHASH_TYPE(name, khkey_t, khval_t) 								\
+	__KHASH_PROTOTYPES(name, khkey_t, khval_t)
+
+#define KHASH_INIT2(name, SCOPE, khkey_t, khval_t, kh_is_map, __hash_func, __hash_equal) \
+	__KHASH_TYPE(name, khkey_t, khval_t) 								\
+	__KHASH_IMPL(name, SCOPE, khkey_t, khval_t, kh_is_map, __hash_func, __hash_equal)
+
+#define KHASH_INIT(name, khkey_t, khval_t, kh_is_map, __hash_func, __hash_equal) \
+	KHASH_INIT2(name, static kh_inline, khkey_t, khval_t, kh_is_map, __hash_func, __hash_equal)
+
+/* --- BEGIN OF HASH FUNCTIONS --- */
+
+/*! @function
+  @abstract     Integer hash function
+  @param  key   The integer [khint32_t]
+  @return       The hash value [khint_t]
+ */
+#define kh_int_hash_func(key) (khint32_t)(key)
+/*! @function
+  @abstract     Integer comparison function
+ */
+#define kh_int_hash_equal(a, b) ((a) == (b))
+/*! @function
+  @abstract     64-bit integer hash function
+  @param  key   The integer [khint64_t]
+  @return       The hash value [khint_t]
+ */
+#define kh_int64_hash_func(key) (khint32_t)((key)>>33^(key)^(key)<<11)
+/*! @function
+  @abstract     64-bit integer comparison function
+ */
+#define kh_int64_hash_equal(a, b) ((a) == (b))
+/*! @function
+  @abstract     const char* hash function
+  @param  s     Pointer to a null terminated string
+  @return       The hash value
+ */
+static kh_inline khint_t __ac_X31_hash_string(const char *s)
+{
+	khint_t h = (khint_t)*s;
+	if (h) for (++s ; *s; ++s) h = (h << 5) - h + (khint_t)*s;
+	return h;
+}
+/*! @function
+  @abstract     Another interface to const char* hash function
+  @param  key   Pointer to a null terminated string [const char*]
+  @return       The hash value [khint_t]
+ */
+#define kh_str_hash_func(key) __ac_X31_hash_string(key)
+/*! @function
+  @abstract     Const char* comparison function
+ */
+#define kh_str_hash_equal(a, b) (strcmp(a, b) == 0)
+
+static kh_inline khint_t __ac_Wang_hash(khint_t key)
+{
+    key += ~(key << 15);
+    key ^=  (key >> 10);
+    key +=  (key << 3);
+    key ^=  (key >> 6);
+    key += ~(key << 11);
+    key ^=  (key >> 16);
+    return key;
+}
+#define kh_int_hash_func2(k) __ac_Wang_hash((khint_t)key)
+
+/* --- END OF HASH FUNCTIONS --- */
+
+/* Other convenient macros... */
+
+/*!
+  @abstract Type of the hash table.
+  @param  name  Name of the hash table [symbol]
+ */
+#define khash_t(name) kh_##name##_t
+
+/*! @function
+  @abstract     Initiate a hash table.
+  @param  name  Name of the hash table [symbol]
+  @return       Pointer to the hash table [khash_t(name)*]
+ */
+#define kh_init(name) kh_init_##name()
+
+/*! @function
+  @abstract     Destroy a hash table.
+  @param  name  Name of the hash table [symbol]
+  @param  h     Pointer to the hash table [khash_t(name)*]
+ */
+#define kh_destroy(name, h) kh_destroy_##name(h)
+
+/*! @function
+  @abstract     Reset a hash table without deallocating memory.
+  @param  name  Name of the hash table [symbol]
+  @param  h     Pointer to the hash table [khash_t(name)*]
+ */
+#define kh_clear(name, h) kh_clear_##name(h)
+
+/*! @function
+  @abstract     Resize a hash table.
+  @param  name  Name of the hash table [symbol]
+  @param  h     Pointer to the hash table [khash_t(name)*]
+  @param  s     New size [khint_t]
+ */
+#define kh_resize(name, h, s) kh_resize_##name(h, s)
+
+/*! @function
+  @abstract     Insert a key to the hash table.
+  @param  name  Name of the hash table [symbol]
+  @param  h     Pointer to the hash table [khash_t(name)*]
+  @param  k     Key [type of keys]
+  @param  r     Extra return code: 0 if the key is present in the hash table;
+                1 if the bucket is empty (never used); 2 if the element in
+				the bucket has been deleted [int*]
+  @return       Iterator to the inserted element [khint_t]
+ */
+#define kh_put(name, h, k, r) kh_put_##name(h, k, r)
+
+/*! @function
+  @abstract     Retrieve a key from the hash table.
+  @param  name  Name of the hash table [symbol]
+  @param  h     Pointer to the hash table [khash_t(name)*]
+  @param  k     Key [type of keys]
+  @return       Iterator to the found element, or kh_end(h) if the element is absent [khint_t]
+ */
+#define kh_get(name, h, k) kh_get_##name(h, k)
+
+/*! @function
+  @abstract     Remove a key from the hash table.
+  @param  name  Name of the hash table [symbol]
+  @param  h     Pointer to the hash table [khash_t(name)*]
+  @param  k     Iterator to the element to be deleted [khint_t]
+ */
+#define kh_del(name, h, k) kh_del_##name(h, k)
+
+/*! @function
+  @abstract     Test whether a bucket contains data.
+  @param  h     Pointer to the hash table [khash_t(name)*]
+  @param  x     Iterator to the bucket [khint_t]
+  @return       1 if containing data; 0 otherwise [int]
+ */
+#define kh_exist(h, x) (!__ac_iseither((h)->flags, (x)))
+
+/*! @function
+  @abstract     Get key given an iterator
+  @param  h     Pointer to the hash table [khash_t(name)*]
+  @param  x     Iterator to the bucket [khint_t]
+  @return       Key [type of keys]
+ */
+#define kh_key(h, x) ((h)->keys[x])
+
+/*! @function
+  @abstract     Get value given an iterator
+  @param  h     Pointer to the hash table [khash_t(name)*]
+  @param  x     Iterator to the bucket [khint_t]
+  @return       Value [type of values]
+  @discussion   For hash sets, calling this results in segfault.
+ */
+#define kh_val(h, x) ((h)->vals[x])
+
+/*! @function
+  @abstract     Alias of kh_val()
+ */
+#define kh_value(h, x) ((h)->vals[x])
+
+/*! @function
+  @abstract     Get the start iterator
+  @param  h     Pointer to the hash table [khash_t(name)*]
+  @return       The start iterator [khint_t]
+ */
+#define kh_begin(h) (khint_t)(0)
+
+/*! @function
+  @abstract     Get the end iterator
+  @param  h     Pointer to the hash table [khash_t(name)*]
+  @return       The end iterator [khint_t]
+ */
+#define kh_end(h) ((h)->n_buckets)
+
+/*! @function
+  @abstract     Get the number of elements in the hash table
+  @param  h     Pointer to the hash table [khash_t(name)*]
+  @return       Number of elements in the hash table [khint_t]
+ */
+#define kh_size(h) ((h)->size)
+
+/*! @function
+  @abstract     Get the number of buckets in the hash table
+  @param  h     Pointer to the hash table [khash_t(name)*]
+  @return       Number of buckets in the hash table [khint_t]
+ */
+#define kh_n_buckets(h) ((h)->n_buckets)
+
+/*! @function
+  @abstract     Iterate over the entries in the hash table
+  @param  h     Pointer to the hash table [khash_t(name)*]
+  @param  kvar  Variable to which key will be assigned
+  @param  vvar  Variable to which value will be assigned
+  @param  code  Block of code to execute
+ */
+#define kh_foreach(h, kvar, vvar, code) { khint_t __i;		\
+	for (__i = kh_begin(h); __i != kh_end(h); ++__i) {		\
+		if (!kh_exist(h,__i)) continue;						\
+		(kvar) = kh_key(h,__i);								\
+		(vvar) = kh_val(h,__i);								\
+		code;												\
+	} }
+
+/*! @function
+  @abstract     Iterate over the entries in the hash table
+  @param  h     Pointer to the hash table [khash_t(name)*]
+  @param  kvar  Variable to which key will be assigned
+  @param  code  Block of code to execute
+ */
+#define kh_foreach_key(h, kvar, code) { khint_t __i;           	\
+       for (__i = kh_begin(h); __i != kh_end(h); ++__i) {		\
+			if (!kh_exist(h,__i)) continue;                  	\
+               (kvar) = kh_key(h,__i);                          \
+               code;                                            \
+       } }
+
+/*! @function
+  @abstract     Iterate over the values in the hash table
+  @param  h     Pointer to the hash table [khash_t(name)*]
+  @param  vvar  Variable to which value will be assigned
+  @param  code  Block of code to execute
+ */
+#define kh_foreach_value(h, vvar, code) { khint_t __i;		\
+	for (__i = kh_begin(h); __i != kh_end(h); ++__i) {		\
+		if (!kh_exist(h,__i)) continue;						\
+		(vvar) = kh_val(h,__i);								\
+		code;												\
+	} }
+
+/*! @function
+  @abstract     Iterate over the values in the hash table
+  @param  h     Pointer to the hash table [khash_t(name)*]
+  @param  rvar  Variable to which value will be assigned
+  @param  code  Block of code to execute
+ */
+#define kh_foreach_value_ref(h, rvar, code) { khint_t __i;	\
+	for (__i = kh_begin(h); __i != kh_end(h); ++__i) {		\
+		if (!kh_exist(h,__i)) continue;						\
+		(rvar) = &kh_val(h,__i);							\
+		code;												\
+	} }
+
+/* More conenient interfaces */
+
+/*! @function
+  @abstract     Instantiate a hash set containing integer keys
+  @param  name  Name of the hash table [symbol]
+ */
+#define KHASH_SET_INIT_INT(name)										\
+	KHASH_INIT(name, khint32_t, char, 0, kh_int_hash_func, kh_int_hash_equal)
+
+#define KHASH_SET_DECLARE_INT(name)										\
+	KHASH_DECLARE(name, khint32_t, char)
+
+#define KHASH_SET_IMPL_INT(name)										\
+	__KHASH_IMPL(name, , khint32_t, char, 0, kh_int_hash_func, kh_int_hash_equal)
+
+/*! @function
+  @abstract     Instantiate a hash map containing integer keys
+  @param  name  Name of the hash table [symbol]
+  @param  khval_t  Type of values [type]
+ */
+#define KHASH_MAP_INIT_INT(name, khval_t)								\
+	KHASH_INIT(name, khint32_t, khval_t, 1, kh_int_hash_func, kh_int_hash_equal)
+
+#define KHASH_MAP_DECLARE_INT(name, khval_t)								\
+	KHASH_DECLARE(name, khint32_t, khval_t)
+
+#define KHASH_MAP_IMPL_INT(name, khval_t)								\
+	__KHASH_IMPL(name, , khint32_t, khval_t, 1, kh_int_hash_func, kh_int_hash_equal)
+
+/*! @function
+  @abstract     Instantiate a hash map containing 64-bit integer keys
+  @param  name  Name of the hash table [symbol]
+ */
+#define KHASH_SET_INIT_INT64(name)										\
+	KHASH_INIT(name, khint64_t, char, 0, kh_int64_hash_func, kh_int64_hash_equal)
+
+#define KHASH_SET_DECLARE_INT64(name)										\
+	KHASH_DECLARE(name, khint64_t, char)
+
+#define KHASH_SET_IMPL_INT64(name)										\
+	__KHASH_IMPL(name, , khint64_t, char, 0, kh_int64_hash_func, kh_int64_hash_equal)
+
+/*! @function
+  @abstract     Instantiate a hash map containing 64-bit integer keys
+  @param  name  Name of the hash table [symbol]
+  @param  khval_t  Type of values [type]
+ */
+#define KHASH_MAP_INIT_INT64(name, khval_t)								\
+	KHASH_INIT(name, khint64_t, khval_t, 1, kh_int64_hash_func, kh_int64_hash_equal)
+
+#define KHASH_MAP_DECLARE_INT64(name, khval_t)								\
+	KHASH_DECLARE(name, khint64_t, khval_t)
+
+#define KHASH_MAP_IMPL_INT64(name, khval_t)								\
+	__KHASH_IMPL(name, , khint64_t, khval_t, 1, kh_int64_hash_func, kh_int64_hash_equal)
+
+typedef const char *kh_cstr_t;
+/*! @function
+  @abstract     Instantiate a hash map containing const char* keys
+  @param  name  Name of the hash table [symbol]
+ */
+#define KHASH_SET_INIT_STR(name)										\
+	KHASH_INIT(name, kh_cstr_t, char, 0, kh_str_hash_func, kh_str_hash_equal)
+
+#define KHASH_SET_DECLARE_STR(name)										\
+	KHASH_DECLARE(name, kh_cstr_t, char)
+
+#define KHASH_SET_IMPL_STR(name)										\
+	__KHASH_IMPL(name, , kh_cstr_t, char, 0, kh_str_hash_func, kh_str_hash_equal)
+
+/*! @function
+  @abstract     Instantiate a hash map containing const char* keys
+  @param  name  Name of the hash table [symbol]
+  @param  khval_t  Type of values [type]
+ */
+#define KHASH_MAP_INIT_STR(name, khval_t)								\
+	KHASH_INIT(name, kh_cstr_t, khval_t, 1, kh_str_hash_func, kh_str_hash_equal)
+
+#define KHASH_MAP_DECLARE_STR(name, khval_t)								\
+	KHASH_DECLARE(name, kh_cstr_t, khval_t)
+
+#define KHASH_MAP_IMPL_STR(name, khval_t)								\
+	__KHASH_IMPL(name, , kh_cstr_t, khval_t, 1, kh_str_hash_func, kh_str_hash_equal)
+
+#endif /* __AC_KHASH_H */
diff --git a/src/include/pathcoll.h b/src/include/pathcoll.h
new file mode 100755
index 00000000..f1a0b93e
--- /dev/null
+++ b/src/include/pathcoll.h
@@ -0,0 +1,23 @@
+#ifndef __PATHCOLL_H_
+#define __PATHCOLL_H_
+
+// utility to handle path collection (like BOX86_PATH or BOX86_LD_LIBRARY_PATH)
+
+// paths can be resized with realloc, so don't take address as invariant
+typedef struct path_collection_s
+{
+    int    size;
+    int    cap;
+    char** paths;
+} path_collection_t;
+
+void ParseList(const char* List, path_collection_t* collection, int folder);
+void FreeCollection(path_collection_t* collection);
+void CopyCollection(path_collection_t* to, path_collection_t* from);
+void AddPath(const char* path, path_collection_t* collection, int folder);
+void PrependPath(const char* path, path_collection_t* collection, int folder);
+void AppendList(path_collection_t* collection, const char* List, int folder);
+void PrependList(path_collection_t* collection, const char* List, int folder);
+int FindInCollection(const char* path, path_collection_t* collection);
+
+#endif //__PATHCOLL_H_
diff --git a/src/include/wine_tools.h b/src/include/wine_tools.h
new file mode 100755
index 00000000..52993cea
--- /dev/null
+++ b/src/include/wine_tools.h
@@ -0,0 +1,13 @@
+#ifndef __WINE_TOOLS_H__
+#define __WINE_TOOLS_H__
+
+void wine_prereserve(const char* reserve);
+
+extern int wine_preloaded;
+void* get_wine_prereserve();
+
+#ifdef DYNAREC
+void dynarec_wine_prereserve();
+#endif
+
+#endif //__WINE_TOOLS_H__
diff --git a/src/main.c b/src/main.c
new file mode 100755
index 00000000..4858e797
--- /dev/null
+++ b/src/main.c
@@ -0,0 +1,734 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <limits.h>
+#include <errno.h>
+#include <unistd.h>
+#include <dirent.h>
+#include <signal.h>
+#include <sys/syscall.h>
+
+#include "build_info.h"
+#include "debug.h"
+#include "fileutils.h"
+#include "box64context.h"
+#include "wine_tools.h"
+
+box64context_t *my_context = NULL;
+int box64_log = LOG_NONE;
+int box64_nobanner = 0;
+int box64_dynarec_log = LOG_INFO; //LOG_NONE;
+int box64_pagesize;
+int box64_dynarec = 0;
+int dlsym_error = 0;
+int trace_xmm = 0;
+int trace_emm = 0;
+#ifdef HAVE_TRACE
+uint64_t start_cnt = 0;
+#ifdef DYNAREC
+int box64_dynarec_trace = 0;
+#endif
+#endif
+int box64_zoom = 0;
+int x11threads = 0;
+int x11glx = 1;
+int allow_missing_libs = 0;
+int fix_64bit_inodes = 0;
+int box64_steam = 0;
+int box64_nopulse = 0;
+int box64_nogtk = 0;
+int box64_novulkan = 0;
+char* libGL = NULL;
+uintptr_t   trace_start = 0, trace_end = 0;
+char* trace_func = NULL;
+uintptr_t fmod_smc_start = 0;
+uintptr_t fmod_smc_end = 0;
+uint32_t default_fs = 0;
+int jit_gdb = 0;
+int box64_tcmalloc_minimal = 0;
+
+FILE* ftrace = NULL;
+int ftrace_has_pid = 0;
+
+void openFTrace()
+{
+    char* t = getenv("BOX64_TRACE_FILE");
+    char tmp[500];
+    char* p = t;
+    if(p && strstr(t, "%pid")) {
+        strcpy(tmp, p);
+        char* c = strstr(tmp, "%pid");
+        *c = 0; // cut
+        char pid[10];
+        sprintf(pid, "%d", getpid());
+        strcat(tmp, pid);
+        c = strstr(p, "%pid") + strlen("%pid");
+        strcat(tmp, c);
+        p = tmp;
+        ftrace_has_pid = 1;
+    }
+    if(p) {
+        ftrace = fopen(p, "w");
+        if(!ftrace) {
+            ftrace = stdout;
+            printf_log(LOG_INFO, "Cannot open trace file \"%s\" for writing (error=%s)\n", p, strerror(errno));
+        } else {
+            if(!box64_nobanner)
+                printf("BOX64 Trace redirected to \"%s\"\n", p);
+        }
+    }
+}
+
+void my_child_fork()
+{
+    if(ftrace_has_pid) {
+        // open a new ftrace...
+        fclose(ftrace);
+        openFTrace();
+    }
+}
+
+
+EXPORTDYN
+void LoadLogEnv()
+{
+    ftrace = stdout;
+    const char *p = getenv("BOX64_NOBANNER");
+    if(p) {
+        if(strlen(p)==1) {
+            if(p[0]>='0' && p[1]<='1')
+                box64_nobanner = p[0]-'0';
+        }
+    }
+    p = getenv("BOX64_LOG");
+    if(p) {
+        if(strlen(p)==1) {
+            if(p[0]>='0'+LOG_NONE && p[1]<='0'+LOG_DEBUG)
+                box64_log = p[0]-'0';
+        } else {
+            if(!strcasecmp(p, "NONE"))
+                box64_log = LOG_NONE;
+            else if(!strcasecmp(p, "INFO"))
+                box64_log = LOG_INFO;
+            else if(!strcasecmp(p, "DEBUG"))
+                box64_log = LOG_DEBUG;
+            else if(!strcasecmp(p, "DUMP"))
+                box64_log = LOG_DUMP;
+        }
+        if(!box64_nobanner)
+            printf_log(LOG_INFO, "Debug level is %d\n", box64_log);
+    }
+#ifdef HAVE_TRACE
+    p = getenv("BOX64_TRACE_XMM");
+    if(p) {
+        if(strlen(p)==1) {
+            if(p[0]>='0' && p[1]<='0'+1)
+                trace_xmm = p[0]-'0';
+        }
+    }
+    p = getenv("BOX64_TRACE_EMM");
+    if(p) {
+        if(strlen(p)==1) {
+            if(p[0]>='0' && p[1]<='0'+1)
+                trace_emm = p[0]-'0';
+        }
+    }
+    p = getenv("BOX64_TRACE_START");
+    if(p) {
+        char* p2;
+        start_cnt = strtoll(p, &p2, 10);
+        printf_log(LOG_INFO, "Will start trace only after %llu instructions\n", start_cnt);
+    }
+#endif
+    // grab BOX64_TRACE_FILE envvar, and change %pid to actual pid is present in the name
+    openFTrace();
+    // Other BOX64 env. var.
+    p = getenv("BOX64_DLSYM_ERROR");
+    if(p) {
+        if(strlen(p)==1) {
+            if(p[0]>='0' && p[1]<='0'+1)
+                dlsym_error = p[0]-'0';
+        }
+    }
+    p = getenv("BOX64_X11THREADS");
+    if(p) {
+        if(strlen(p)==1) {
+            if(p[0]>='0' && p[1]<='0'+1)
+                x11threads = p[0]-'0';
+        }
+        if(x11threads)
+            printf_log(LOG_INFO, "Try to Call XInitThreads if libX11 is loaded\n");
+    }
+    p = getenv("BOX64_X11GLX");
+    if(p) {
+        if(strlen(p)==1) {
+            if(p[0]>='0' && p[1]<='0'+1)
+                x11glx = p[0]-'0';
+        }
+        if(x11glx)
+            printf_log(LOG_INFO, "Hack to force libX11 GLX extension present\n");
+        else
+            printf_log(LOG_INFO, "Disabled Hack to force libX11 GLX extension present\n");
+    }
+    p = getenv("BOX64_LIBGL");
+    if(p)
+        libGL = strdup(p);
+    if(!libGL) {
+        p = getenv("SDL_VIDEO_GL_DRIVER");
+        if(p)
+            libGL = strdup(p);
+    }
+    if(libGL) {
+        printf_log(LOG_INFO, "BOX64 using \"%s\" as libGL.so.1\n", p);
+    }
+    p = getenv("BOX64_ALLOWMISSINGLIBS");
+        if(p) {
+        if(strlen(p)==1) {
+            if(p[0]>='0' && p[1]<='0'+1)
+                allow_missing_libs = p[0]-'0';
+        }
+        if(allow_missing_libs)
+            printf_log(LOG_INFO, "Allow missing needed libs\n");
+    }
+    p = getenv("BOX64_NOPULSE");
+        if(p) {
+        if(strlen(p)==1) {
+            if(p[0]>='0' && p[1]<='0'+1)
+                box64_nopulse = p[0]-'0';
+        }
+        if(box64_nopulse)
+            printf_log(LOG_INFO, "Disable the use of pulseaudio libs\n");
+    }
+    p = getenv("BOX64_NOGTK");
+        if(p) {
+        if(strlen(p)==1) {
+            if(p[0]>='0' && p[1]<='0'+1)
+                box64_nogtk = p[0]-'0';
+        }
+        if(box64_nogtk)
+            printf_log(LOG_INFO, "Disable the use of wrapped gtk libs\n");
+    }
+    p = getenv("BOX64_NOVULKAN");
+        if(p) {
+        if(strlen(p)==1) {
+            if(p[0]>='0' && p[1]<='0'+1)
+                box64_novulkan = p[0]-'0';
+        }
+        if(box64_novulkan)
+            printf_log(LOG_INFO, "Disable the use of wrapped vulkan libs\n");
+    }
+    p = getenv("BOX64_FIX_64BIT_INODES");
+        if(p) {
+        if(strlen(p)==1) {
+            if(p[0]>='0' && p[1]<='0'+1)
+                fix_64bit_inodes = p[0]-'0';
+        }
+        if(fix_64bit_inodes)
+            printf_log(LOG_INFO, "Fix 64bit inodes\n");
+    }
+    p = getenv("BOX64_JITGDB");
+        if(p) {
+        if(strlen(p)==1) {
+            if(p[0]>='0' && p[1]<='0'+1)
+                jit_gdb = p[0]-'0';
+        }
+        if(jit_gdb)
+            printf_log(LOG_INFO, "Launch %s on segfault\n", (jit_gdb==2)?"gdbserver":"gdb");
+    }
+    box64_pagesize = sysconf(_SC_PAGESIZE);
+    if(!box64_pagesize)
+        box64_pagesize = 4096;
+}
+
+EXPORTDYN
+void LoadEnvPath(path_collection_t *col, const char* defpath, const char* env)
+{
+    const char* p = getenv(env);
+    if(p) {
+        printf_log(LOG_INFO, "%s: ", env);
+        ParseList(p, col, 1);
+    } else {
+        printf_log(LOG_INFO, "Using default %s: ", env);
+        ParseList(defpath, col, 1);
+    }
+    if(LOG_INFO<=box64_log) {
+        for(int i=0; i<col->size; i++)
+            printf_log(LOG_INFO, "%s%s", col->paths[i], (i==col->size-1)?"\n":":");
+    }
+}
+
+EXPORTDYN
+int CountEnv(const char** env)
+{
+    // count, but remove all BOX64_* environnement
+    // also remove PATH and LD_LIBRARY_PATH
+    // but add 2 for default BOX64_PATH and BOX64_LD_LIBRARY_PATH
+    const char** p = env;
+    int c = 0;
+    while(*p) {
+        if(strncmp(*p, "BOX64_", 6)!=0)
+            //if(!(strncmp(*p, "PATH=", 5)==0 || strncmp(*p, "LD_LIBRARY_PATH=", 16)==0))
+                ++c;
+        ++p;
+    }
+    return c+2;
+}
+EXPORTDYN
+int GatherEnv(char*** dest, const char** env, const char* prog)
+{
+    // Add all but BOX64_* environnement
+    // but add 2 for default BOX64_PATH and BOX64_LD_LIBRARY_PATH
+    const char** p = env;    
+    int idx = 0;
+    int path = 0;
+    int ld_path = 0;
+    while(*p) {
+        if(strncmp(*p, "BOX64_PATH=", 11)==0) {
+            (*dest)[idx++] = strdup(*p+6);
+            path = 1;
+        } else if(strncmp(*p, "BOX64_LD_LIBRARY_PATH=", 22)==0) {
+            (*dest)[idx++] = strdup(*p+6);
+            ld_path = 1;
+        } else if(strncmp(*p, "_=", 2)==0) {
+            /*int l = strlen(prog);
+            char tmp[l+3];
+            strcpy(tmp, "_=");
+            strcat(tmp, prog);
+            (*dest)[idx++] = strdup(tmp);*/
+        } else if(strncmp(*p, "BOX64_", 6)!=0) {
+            (*dest)[idx++] = strdup(*p);
+            /*if(!(strncmp(*p, "PATH=", 5)==0 || strncmp(*p, "LD_LIBRARY_PATH=", 16)==0)) {
+            }*/
+        }
+        ++p;
+    }
+    // update the calloc of envv when adding new variables here
+    if(!path) {
+        (*dest)[idx++] = strdup("BOX64_PATH=.:bin");
+    }
+    if(!ld_path) {
+        (*dest)[idx++] = strdup("BOX64_LD_LIBRARY_PATH=.:lib");
+    }
+    // add "_=prog" at the end...
+    if(prog) {
+        int l = strlen(prog);
+        char tmp[l+3];
+        strcpy(tmp, "_=");
+        strcat(tmp, prog);
+        (*dest)[idx++] = strdup(tmp);
+    }
+    // and a final NULL
+    (*dest)[idx++] = 0;
+    return 0;
+}
+
+
+void PrintHelp() {
+    printf("\n\nThis is Box86, the Linux x86 emulator with a twist\n");
+    printf("\nUsage is box86 [options] path/to/software [args]\n");
+    printf("to launch x86 software\n");
+    printf(" options can be :\n");
+    printf("    '-v'|'--version' to print box86 version and quit\n");
+    printf("    '-h'|'--help'    to print box86 help and quit\n");
+    printf("You can also set some environment variables:\n");
+    printf(" BOX64_PATH is the box86 version of PATH (default is '.:bin')\n");
+    printf(" BOX64_LD_LIBRARY_PATH is the box86 version LD_LIBRARY_PATH (default is '.:lib')\n");
+    printf(" BOX64_LOG with 0/1/2/3 or NONE/INFO/DEBUG/DUMP to set the printed debug info\n");
+    printf(" BOX64_NOBANNER with 0/1 to enable/disable the printing of box86 version and build at start\n");
+#ifdef HAVE_TRACE
+    printf(" BOX64_TRACE with 1 to enable x86 execution trace\n");
+    printf("    or with XXXXXX-YYYYYY to enable x86 execution trace only between address\n");
+    printf("    or with FunctionName to enable x86 execution trace only in one specific function\n");
+    printf("  use BOX64_TRACE_INIT instead of BOX_TRACE to start trace before init of Libs and main program\n\t (function name will probably not work then)\n");
+    printf(" BOX64_TRACE_XMM with 1 to enable dump of SSE/SSE2 register along with regular registers\n");
+    printf(" BOX64_TRACE_START with N to enable trace after N instructions\n");
+#endif
+    printf(" BOX64_TRACE_FILE with FileName to redirect logs in a file");
+    printf(" BOX64_DLSYM_ERROR with 1 to log dlsym errors\n");
+    printf(" BOX64_LOAD_ADDR=0xXXXXXX try to load at 0xXXXXXX main binary (if binary is a PIE)\n");
+    printf(" BOX64_NOSIGSEGV=1 to disable handling of SigSEGV\n");
+    printf(" BOX64_NOSIGILL=1  to disable handling of SigILL\n");
+    printf(" BOX64_X11THREADS=1 to call XInitThreads when loading X11 (for old Loki games with Loki_Compat lib)");
+    printf(" BOX64_LIBGL=libXXXX set the name (and optionnaly full path) for libGL.so.1\n");
+    printf(" BOX64_LD_PRELOAD=XXXX[:YYYYY] force loading XXXX (and YYYY...) libraries with the binary\n");
+    printf(" BOX64_ALLOWMISSINGLIBS with 1 to allow to continue even if a lib is missing (unadvised, will probably  crash later)\n");
+    printf(" BOX64_NOPULSE=1 to disable the loading of pulseaudio libs\n");
+    printf(" BOX64_NOGTK=1 to disable the loading of wrapped gtk libs\n");
+    printf(" BOX64_NOVULKAN=1 to disable the loading of wrapped vulkan libs\n");
+    printf(" BOX64_JITGDB with 1 to launch \"gdb\" when a segfault is trapped, attached to the offending process\n");
+}
+
+EXPORTDYN
+void LoadEnvVars(box64context_t *context)
+{
+    // check BOX64_LD_LIBRARY_PATH and load it
+    LoadEnvPath(&context->box64_ld_lib, ".:lib:lib64:x86_64", "BOX64_LD_LIBRARY_PATH");
+    if(FileExist("/lib/x86_64-linux-gnu", 0))
+        AddPath("/lib/x86_64-linux-gnu", &context->box64_ld_lib, 1);
+    if(FileExist("/usr/lib/x86_64-linux-gnu", 0))
+        AddPath("/usr/lib/x86_64-linux-gnu", &context->box64_ld_lib, 1);
+    if(FileExist("/lib/i686-pc-linux-gnu", 0))
+        AddPath("/lib/i686-pc-linux-gnu", &context->box64_ld_lib, 1);
+    if(FileExist("/usr/lib/i686-pc-linux-gnu", 0))
+        AddPath("/usr/lib/i686-pc-linux-gnu", &context->box64_ld_lib, 1);
+    if(FileExist("/usr/lib32", 0))
+        AddPath("/usr/lib32", &context->box64_ld_lib, 1);
+    if(getenv("LD_LIBRARY_PATH"))
+        PrependList(&context->box64_ld_lib, getenv("LD_LIBRARY_PATH"), 1);   // in case some of the path are for x86 world
+    if(getenv("BOX64_EMULATED_LIBS")) {
+        char* p = getenv("BOX64_EMULATED_LIBS");
+        ParseList(p, &context->box64_emulated_libs, 0);
+        if (my_context->box64_emulated_libs.size && box64_log) {
+            printf_log(LOG_INFO, "BOX64 will force the used of emulated libs for ");
+            for (int i=0; i<context->box64_emulated_libs.size; ++i)
+                printf_log(LOG_INFO, "%s ", context->box64_emulated_libs.paths[i]);
+            printf_log(LOG_INFO, "\n");
+        }
+    }
+
+    if(getenv("BOX64_NOSIGSEGV")) {
+        if (strcmp(getenv("BOX64_NOSIGSEGV"), "1")==0)
+            context->no_sigsegv = 1;
+            printf_log(LOG_INFO, "BOX64: Disabling handling of SigSEGV\n");
+    }
+    if(getenv("BOX64_NOSIGILL")) {
+        if (strcmp(getenv("BOX64_NOSIGILL"), "1")==0)
+            context->no_sigill = 1;
+            printf_log(LOG_INFO, "BOX64: Disabling handling of SigILL\n");
+    }
+    // check BOX64_PATH and load it
+    LoadEnvPath(&context->box64_path, ".:bin", "BOX64_PATH");
+    if(getenv("PATH"))
+        AppendList(&context->box64_path, getenv("PATH"), 1);   // in case some of the path are for x86 world
+#ifdef HAVE_TRACE
+    char* p = getenv("BOX64_TRACE");
+    if(p) {
+        if (strcmp(p, "0"))
+            context->x86trace = 1;
+    }
+    p = getenv("BOX64_TRACE_INIT");
+    if(p) {
+        if (strcmp(p, "0"))
+            context->x86trace = 1;
+    }
+    if(my_context->x86trace) {
+        /*printf_log(LOG_INFO, "Initializing Zydis lib\n");
+        if(InitX86Trace(my_context)) {
+            printf_log(LOG_INFO, "Zydis init failed, no x86 trace activated\n");
+            context->x86trace = 0;
+        }*/
+            context->x86trace = 0;  // not implemented yet
+    }
+#endif
+}
+
+EXPORTDYN
+void setupTraceInit(box64context_t* context)
+{
+#ifdef HAVE_TRACE
+    char* p = getenv("BOX64_TRACE_INIT");
+    if(p) {
+        setbuf(stdout, NULL);
+        uintptr_t trace_start=0, trace_end=0;
+        if (strcmp(p, "1")==0)
+            SetTraceEmu(0, 0);
+        else if (strchr(p,'-')) {
+            if(sscanf(p, "%d-%d", &trace_start, &trace_end)!=2) {
+                if(sscanf(p, "0x%X-0x%X", &trace_start, &trace_end)!=2)
+                    sscanf(p, "%x-%x", &trace_start, &trace_end);
+            }
+            if(trace_start || trace_end)
+                SetTraceEmu(trace_start, trace_end);
+        } else {
+            if (0/*GetSymbolStartEnd(GetMapSymbol(my_context->maplib), p, &trace_start, &trace_end)*/) {
+                SetTraceEmu(trace_start, trace_end);
+                printf_log(LOG_INFO, "TRACE on %s only (%p-%p)\n", p, (void*)trace_start, (void*)trace_end);
+            } else {
+                printf_log(LOG_NONE, "Warning, symbol to Traced (\"%s\") not found, disabling trace\n", p);
+                SetTraceEmu(0, 100);  // disabling trace, mostly
+            }
+        }
+    } else {
+        p = getenv("BOX64_TRACE");
+        if(p)
+            if (strcmp(p, "0"))
+                SetTraceEmu(0, 1);
+    }
+#endif
+}
+
+EXPORTDYN
+void setupTrace(box64context_t* context)
+{
+#ifdef HAVE_TRACE
+    char* p = getenv("BOX64_TRACE");
+    if(p) {
+        setbuf(stdout, NULL);
+        uintptr_t trace_start=0, trace_end=0;
+        if (strcmp(p, "1")==0)
+            SetTraceEmu(0, 0);
+        else if (strchr(p,'-')) {
+            if(sscanf(p, "%d-%d", &trace_start, &trace_end)!=2) {
+                if(sscanf(p, "0x%X-0x%X", &trace_start, &trace_end)!=2)
+                    sscanf(p, "%x-%x", &trace_start, &trace_end);
+            }
+            if(trace_start || trace_end) {
+                SetTraceEmu(trace_start, trace_end);
+                if(!trace_start && trace_end==1) {
+                    printf_log(LOG_INFO, "TRACE enabled but inactive\n");
+                } else {
+                    printf_log(LOG_INFO, "TRACE on %s only (%p-%p)\n", p, (void*)trace_start, (void*)trace_end);
+                }
+            }
+        } else {
+            if (0/*GetGlobalSymbolStartEnd(my_context->maplib, p, &trace_start, &trace_end)*/) {
+                SetTraceEmu(trace_start, trace_end);
+                printf_log(LOG_INFO, "TRACE on %s only (%p-%p)\n", p, (void*)trace_start, (void*)trace_end);
+            } else if(0/*GetLocalSymbolStartEnd(my_context->maplib, p, &trace_start, &trace_end, NULL)*/) {
+                SetTraceEmu(trace_start, trace_end);
+                printf_log(LOG_INFO, "TRACE on %s only (%p-%p)\n", p, (void*)trace_start, (void*)trace_end);
+            } else {
+                printf_log(LOG_NONE, "Warning, symbol to Traced (\"%s\") not found, trying to set trace later\n", p);
+                SetTraceEmu(0, 1);  // disabling trace, mostly
+                trace_func = strdup(p);
+            }
+        }
+    }
+#endif
+}
+
+void endBox86()
+{
+    if(!my_context)
+        return;
+
+    // all done, free context
+    FreeBox64Context(&my_context);
+    if(libGL) {
+        free(libGL);
+        libGL = NULL;
+    }
+}
+
+
+static void free_contextargv()
+{
+    for(int i=0; i<my_context->argc; ++i)
+        free(my_context->argv[i]);
+}
+
+const char **environ __attribute__((weak)) = NULL;
+int main(int argc, const char **argv, const char **env) {
+
+    //init_auxval(argc, argv, environ?environ:env);
+    // trying to open and load 1st arg
+    if(argc==1) {
+        PrintBox64Version();
+        PrintHelp();
+        return 1;
+    }
+
+    // init random seed
+    srandom(time(NULL));
+
+    // check BOX64_LOG debug level
+    LoadLogEnv();
+    
+    const char* prog = argv[1];
+    int nextarg = 1;
+    // check if some options are passed
+    while(prog && prog[0]=='-') {
+        if(!strcmp(prog, "-v") || !strcmp(prog, "--version")) {
+            PrintBox64Version();
+            exit(0);
+        }
+        if(!strcmp(prog, "-h") || !strcmp(prog, "--help")) {
+            PrintHelp();
+            exit(0);
+        }
+        // other options?
+        if(!strcmp(prog, "--")) {
+            prog = argv[++nextarg];
+            break;
+        }
+        printf("Warning, unrecognized option '%s'\n", prog);
+        prog = argv[++nextarg];
+    }
+    if(!prog || nextarg==argc) {
+        printf("Box64: nothing to run\n");
+        exit(0);
+    }
+    if(!box64_nobanner)
+        PrintBox64Version();
+    // precheck, for win-preload
+    if(strstr(prog, "wine-preloader")==(prog+strlen(prog)-strlen("wine-preloader"))) {
+        // wine-preloader detecter, skipping it if next arg exist and is an x86 binary
+        int x64 = (nextarg<argc)?FileIsX64ELF(argv[nextarg]):0;
+        if(x64) {
+            prog = argv[++nextarg];
+            printf_log(LOG_INFO, "BOX64: Wine preloader detected, loading \"%s\" directly\n", prog);
+            //wine_preloaded = 1;
+        }
+    }
+    // check if this is wine
+    if(!strcmp(prog, "wine") || (strlen(prog)>5 && !strcmp(prog+strlen(prog)-strlen("/wine"), "/wine"))) {
+        const char* prereserve = getenv("WINEPRELOADRESERVE");
+        printf_log(LOG_INFO, "BOX64: Wine detected, WINEPRELOADRESERVE=\"%s\"\n", prereserve?prereserve:"");
+        if(wine_preloaded)
+            wine_prereserve(prereserve);
+        // special case for winedbg, doesn't work anyway
+        if(argv[nextarg+1] && strstr(argv[nextarg+1], "winedbg")==argv[nextarg+1]) {
+            printf_log(LOG_NONE, "winedbg detected, not launching it!\n");
+            exit(0);    // exiting, it doesn't work anyway
+        }
+    }
+    // Create a new context
+    my_context = NewBox64Context(argc - nextarg);
+
+    // check BOX64_LD_LIBRARY_PATH and load it
+    LoadEnvVars(my_context);
+
+    if(argv[0][0]=='/')
+        my_context->box64path = strdup(argv[0]);
+    else
+        my_context->box64path = ResolveFile(argv[0], &my_context->box64_path);
+    // prepare all other env. var
+    my_context->envc = CountEnv(environ?environ:env);
+    printf_log(LOG_INFO, "Counted %d Env var\n", my_context->envc);
+    // allocate extra space for new environment variables such as BOX64_PATH
+    my_context->envv = (char**)calloc(my_context->envc+4, sizeof(char*));
+    GatherEnv(&my_context->envv, environ?environ:env, my_context->box64path);
+    if(box64_log>=LOG_DUMP) {
+        for (int i=0; i<my_context->envc; ++i)
+            printf_log(LOG_DUMP, " Env[%02d]: %s\n", i, my_context->envv[i]);
+    }
+
+    path_collection_t ld_preload = {0};
+    if(getenv("BOX64_LD_PRELOAD")) {
+        char* p = getenv("BOX64_LD_PRELOAD");
+        ParseList(p, &ld_preload, 0);
+        if (ld_preload.size && box64_log) {
+            printf_log(LOG_INFO, "BOX64 try to Preload ");
+            for (int i=0; i<ld_preload.size; ++i)
+                printf_log(LOG_INFO, "%s ", ld_preload.paths[i]);
+            printf_log(LOG_INFO, "\n");
+        }
+    } else {
+        if(getenv("LD_PRELOAD")) {
+            char* p = getenv("LD_PRELOAD");
+            if(strstr(p, "libtcmalloc_minimal.so.4"))
+                box64_tcmalloc_minimal = 1;
+            if(strstr(p, "libtcmalloc_minimal_debug.so.4"))
+                box64_tcmalloc_minimal = 1;
+            if(strstr(p, "libasan.so"))
+                box64_tcmalloc_minimal = 1; // it seems Address Sanitizer doesn't handle dlsym'd malloc very well
+            ParseList(p, &ld_preload, 0);
+            if (ld_preload.size && box64_log) {
+                printf_log(LOG_INFO, "BOX64 try to Preload ");
+                for (int i=0; i<ld_preload.size; ++i)
+                    printf_log(LOG_INFO, "%s ", ld_preload.paths[i]);
+                printf_log(LOG_INFO, "\n");
+            }
+        }
+    }
+    // lets build argc/argv stuff
+    printf_log(LOG_INFO, "Looking for %s\n", prog);
+    if(strchr(prog, '/'))
+        my_context->argv[0] = strdup(prog);
+    else
+        my_context->argv[0] = ResolveFile(prog, &my_context->box64_path);
+
+    const char* prgname = strrchr(prog, '/');
+    if(!prgname)
+        prgname = prog;
+    else
+        ++prgname;
+    // special case for LittleInferno that use an old libvorbis
+    if(strstr(prgname, "LittleInferno.bin.x86")==prgname) {
+        printf_log(LOG_INFO, "LittleInferno detected, forcing emulated libvorbis\n");
+        AddPath("libvorbis.so.0", &my_context->box64_emulated_libs, 0);
+    }
+    // special case for dontstarve that use an old SDL2
+    if(strstr(prgname, "dontstarve")) {
+        printf_log(LOG_INFO, "Dontstarve* detected, forcing emulated SDL2\n");
+        AddPath("libSDL2-2.0.so.0", &my_context->box64_emulated_libs, 0);
+    }
+    // special case for steam that somehow seems to alter libudev opaque pointer (udev_monitor)
+    if(strstr(prgname, "steam")==prgname) {
+        printf_log(LOG_INFO, "steam detected, forcing emulated libudev\n");
+        AddPath("libudev.so.0", &my_context->box64_emulated_libs, 0);
+        box64_steam = 1;
+    }
+    // special case for steam-runtime-check-requirements to fake 64bits suport
+    if(strstr(prgname, "steam-runtime-check-requirements")==prgname) {
+        printf_log(LOG_INFO, "steam-runtime-check-requirements detected, faking All is good!\n");
+        exit(0);    // exiting, not testing anything
+    }
+    // special case for UnrealLinux.bin, it doesn't like "full path resolution"
+    if(!strcmp(prog, "UnrealLinux.bin") && my_context->argv[0]) {
+        free(my_context->argv[0]);
+        my_context->argv[0] = strdup("./UnrealLinux.bin");
+    }
+    #ifdef RPI
+    // special case for TokiTori 2+, that check if texture max size is > = 8192
+    if(strstr(prgname, "TokiTori2.bin.x86")==prgname) {
+        printf_log(LOG_INFO, "TokiTori 2+ detected, runtime patch to fix GPU non-power-of-two faillure\n");
+        box64_tokitori2 = 1;
+    }
+    #endif
+    // special case for zoom
+    if(strstr(prgname, "zoom")==prgname) {
+        printf_log(LOG_INFO, "Zoom detected, trying to use system libturbojpeg if possible\n");
+        box64_zoom = 1;
+    }
+    /*if(strstr(prgname, "awesomium_process")==prgname) {
+        printf_log(LOG_INFO, "awesomium_process detected, forcing emulated libpng12\n");
+        AddPath("libpng12.so.0", &my_context->box64_emulated_libs, 0);
+    }*/
+    /*if(!strcmp(prgname, "gdb")) {
+        exit(-1);
+    }*/
+
+    for(int i=1; i<my_context->argc; ++i) {
+        my_context->argv[i] = strdup(argv[i+nextarg]);
+        printf_log(LOG_INFO, "argv[%i]=\"%s\"\n", i, my_context->argv[i]);
+    }
+
+    // check if file exist
+    if(!my_context->argv[0] || !FileExist(my_context->argv[0], IS_FILE)) {
+        printf_log(LOG_NONE, "Error: file is not found (check BOX64_PATH)\n");
+        free_contextargv();
+        FreeBox64Context(&my_context);
+        FreeCollection(&ld_preload);
+        return -1;
+    }
+    if(!FileExist(my_context->argv[0], IS_FILE|IS_EXECUTABLE)) {
+        printf_log(LOG_NONE, "Error: %s is not an executable file\n", my_context->argv[0]);
+        free_contextargv();
+        FreeBox64Context(&my_context);
+        FreeCollection(&ld_preload);
+        return -1;
+    }
+    if(!(my_context->fullpath = realpath(my_context->argv[0], NULL)))
+        my_context->fullpath = strdup(my_context->argv[0]);
+    FILE *f = fopen(my_context->argv[0], "rb");
+    if(!f) {
+        printf_log(LOG_NONE, "Error: Cannot open %s\n", my_context->argv[0]);
+        free_contextargv();
+        FreeBox64Context(&my_context);
+        FreeCollection(&ld_preload);
+        return -1;
+    }
+    /*elfheader_t *elf_header = LoadAndCheckElfHeader(f, my_context->argv[0], 1);
+    if(!elf_header) {
+        printf_log(LOG_NONE, "Error: reading elf header of %s, try to launch natively instead\n", my_context->argv[0]);
+        fclose(f);
+        free_contextargv();
+        FreeBox64Context(&my_context);
+        FreeCollection(&ld_preload);
+        return execvp(argv[1], (char * const*)(argv+1));
+    }
+    AddElfHeader(my_context, elf_header);*/
+    return 0;
+}
diff --git a/src/tools/fileutils.c b/src/tools/fileutils.c
new file mode 100755
index 00000000..6a964553
--- /dev/null
+++ b/src/tools/fileutils.c
@@ -0,0 +1,129 @@
+#define _FILE_OFFSET_BITS 64
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h> 
+#include <sys/stat.h> 
+#include <unistd.h>
+#include <limits.h>
+#include <stdint.h>
+
+#ifndef MAX_PATH
+#define MAX_PATH 4096
+#endif
+
+#include "debug.h"
+#include "fileutils.h"
+
+static const char* x64sign = "\x7f" "ELF" "\x02" "\x01" "\x01" "\x00" "\x00" "\x00" "\x00" "\x00" "\x00" "\x00" "\x00" "\x00" "\x02" "\x00" "\x3e" "\x00";
+
+int FileExist(const char* filename, int flags)
+{
+    struct stat sb;
+    if (stat(filename, &sb) == -1)
+        return 0;
+    if(flags==-1)
+        return 1;
+    // check type of file? should be executable, or folder
+    if(flags&IS_FILE) {
+        if(!S_ISREG(sb.st_mode))
+            return 0;
+    } else if(!S_ISDIR(sb.st_mode))
+            return 0;
+    
+    if(flags&IS_EXECUTABLE) {
+        if((sb.st_mode&S_IXUSR)!=S_IXUSR)
+            return 0;   // nope
+    }
+    return 1;
+}
+
+char* ResolveFile(const char* filename, path_collection_t* paths)
+{
+    char p[MAX_PATH];
+    if(filename[0]=='/')
+        return strdup(filename);
+    for (int i=0; i<paths->size; ++i) {
+        if(paths->paths[i][0]!='/') {
+            // not an absolute path...
+            getcwd(p, sizeof(p));
+            if(p[strlen(p)-1]!='/')
+                strcat(p, "/");
+            strcat(p, paths->paths[i]);
+        } else
+            strcpy(p, paths->paths[i]);
+        strcat(p, filename);
+        if(FileExist(p, IS_FILE))
+            return realpath(p, NULL);
+    }
+
+    return NULL;
+}
+
+int FileIsX64ELF(const char* filename)
+{
+    FILE *f = fopen(filename, "rb");
+    if(!f)
+        return 0;
+    char head[sizeof(*x64sign)] = {0};
+    int sz = fread(head, sizeof(*x64sign), 1, f);
+    if(sz!=1) {
+        fclose(f);
+        return 0;
+    }
+    fclose(f);
+    if(memcmp(head, x64sign, sizeof(*x64sign))==0)
+        return 1;
+    return 0;
+}
+
+#if defined(RPI) || defined(RK3399)
+void sanitize_mojosetup_gtk_background()
+{
+    // get GTK2_RC_FILES folder
+    const char* gtk2_rc = getenv("GTK2_RC_FILES");
+    // check if $GTK2_RC_FILES/pixmaps/background.png exist
+    char background[1000] = {0};
+    strcpy(background, gtk2_rc);
+    char* p = strrchr(background, '/'); // remove "/gtkrc"
+    // every error will just silently abort
+    if(!p)
+        return;
+    *p = 0;
+    strcat(background, "/pixmaps/background.png");
+    if(!FileExist(background, IS_FILE))
+        return;
+    // now open are read the header of the PNG to grab the width and height
+    //very crude reading here!
+    FILE* f = fopen(background, "rb");
+    if(!f)
+        return;
+    char sign[8];
+    if(fread(sign, 8, 1, f)!=1) {
+        fclose(f); return;
+    }
+    const char ref[8] = {'\211', 'P', 'N', 'G', '\r', '\n', '\032', '\n' };
+    if (memcmp(sign, ref, 8)) {
+        fclose(f); return;
+    }
+    int32_t width, height;
+    fseek(f, 16, SEEK_SET);
+    if(fread(&width, sizeof(width), 1, f)!=1) {
+        fclose(f); return;
+    }
+    if(fread(&height, sizeof(height), 1, f)!=1) {
+        fclose(f); return;
+    }
+    fclose(f);
+    // need to swap bitness!
+    width = __builtin_bswap32(width);
+    height = __builtin_bswap32(height);
+    printf_log(LOG_INFO, "Mojosetup detected, size of background picture is %dx%d\n", width, height);
+    if(width!=5000 || height!=3000)
+        return; // not a background that will cause any issue
+    // delete the file!
+    f = fopen(background, "r+b");
+    remove(background);
+    printf_log(LOG_INFO, "background deleted!\n");
+}
+#endif
diff --git a/src/tools/pathcoll.c b/src/tools/pathcoll.c
new file mode 100755
index 00000000..d7e423bb
--- /dev/null
+++ b/src/tools/pathcoll.c
@@ -0,0 +1,182 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <limits.h>
+#include <stdint.h>
+
+#ifndef MAX_PATH
+#define MAX_PATH 4096
+#endif
+
+#include "debug.h"
+#include "pathcoll.h"
+
+void FreeCollection(path_collection_t* collection)
+{
+    if(!collection)
+        return;
+    if(collection->cap) {
+        for(int i=0; i<collection->size; ++i)
+            free(collection->paths[i]);
+        free(collection->paths);
+    }
+    collection->paths = NULL;
+    collection->size = 0;
+    collection->cap = 0;
+    return;
+}
+
+void ParseList(const char* List, path_collection_t* collection, int folder)
+{
+    if(collection->cap)
+        FreeCollection(collection);
+    // count the stings (basically count(':') + 1) to define cap
+    int cnt = 1;
+    {
+        const char* p = List;
+        while(p) {
+            p = strchr(p, ':');
+            ++cnt;
+            if(p) ++p;
+        }
+    }
+    // alloc empty strings
+    collection->cap = cnt;
+    collection->paths = (char**)calloc(cnt, sizeof(char*));
+    // and now split the paths...
+    char tmp[MAX_PATH];
+    const char *p = List;
+    int idx = 0;
+    while(p) {
+        const char *p2 = strchr(p, ':');
+        if(!p2) {
+            strncpy(tmp, p, MAX_PATH - 1);
+            p=NULL;
+        } else {
+            int l = (uintptr_t)p2 - (uintptr_t)p;
+            strncpy(tmp, p, l);
+            tmp[l]='\0';
+            p=p2+1;
+        }
+        // check if there is terminal '/', add it if not
+        int l = strlen(tmp);
+        // skip empty strings
+        if(l) {
+            if(folder && tmp[l-1]!='/')
+                strcat(tmp, "/");
+            collection->paths[idx]  =strdup(tmp);
+            collection->size=++idx;
+        }
+    }
+}
+
+void CopyCollection(path_collection_t* to, path_collection_t* from)
+{
+    to->cap = from->cap;
+    to->paths = (char**)calloc(to->cap, sizeof(char*));
+    to->size = from->size;
+    for (int i=0; i<to->size; ++i)
+        to->paths[i] = strdup(from->paths[i]);
+}
+
+void AddPath(const char* path, path_collection_t* collection, int folder)
+{
+    char tmp[MAX_PATH];
+    strcpy(tmp, path);
+    int l = strlen(tmp);
+    // skip empty strings
+    if(l) {
+        if(folder && tmp[l-1]!='/')
+            strcat(tmp, "/");
+        if(collection->size==collection->cap) {
+            collection->cap += 4;
+            collection->paths = (char**)realloc(collection->paths, collection->cap*sizeof(char*));
+        }
+        collection->paths[collection->size++]=strdup(tmp);
+    }
+}
+void PrependPath(const char* path, path_collection_t* collection, int folder)
+{
+    char tmp[MAX_PATH];
+    strcpy(tmp, path);
+    int l = strlen(tmp);
+    // skip empty strings
+    if(l) {
+        if(folder && tmp[l-1]!='/')
+            strcat(tmp, "/");
+        if(collection->size==collection->cap) {
+            collection->cap += 4;
+            collection->paths = (char**)realloc(collection->paths, collection->cap*sizeof(char*));
+        }
+        memmove(collection->paths+1, collection->paths, sizeof(char*)*collection->size);
+        collection->paths[0]=strdup(tmp);
+        ++collection->size;
+    }
+}
+
+void AppendList(path_collection_t* collection, const char* List, int folder)
+{
+    if(!List)
+        return;
+        // and now split the paths...
+    char tmp[MAX_PATH];
+    const char *p = List;
+    while(p) {
+        const char *p2 = strchr(p, ':');
+        if(!p2) {
+            strncpy(tmp, p, MAX_PATH - 1);
+            p=NULL;
+        } else {
+            int l = (uintptr_t)p2 - (uintptr_t)p;
+            strncpy(tmp, p, l);
+            tmp[l]='\0';
+            p=p2+1;
+        }
+        // check if there is terminal '/', add it if not
+        int l = strlen(tmp);
+        // skip empty strings
+        if(l) {
+            if(folder && tmp[l-1]!='/')
+                strcat(tmp, "/");
+            AddPath(tmp, collection, folder);
+        }
+    }
+
+}
+void PrependList(path_collection_t* collection, const char* List, int folder)
+{
+    if(!List)
+        return;
+        // and now split the paths...
+    char tmp[MAX_PATH];
+    char *p = strdup(List);
+    while(p) {
+        char *p2 = strrchr(p, ':');
+        if(!p2) {
+            strncpy(tmp, p, MAX_PATH - 1);
+            free(p);
+            p=NULL;
+        } else {
+            strncpy(tmp, p2+1, MAX_PATH - 1);
+            tmp[MAX_PATH - 1]='\0';
+            *p2 = '\0';
+        }
+        // check if there is terminal '/', add it if not
+        int l = strlen(tmp);
+        // skip empty strings
+        if(l) {
+            if(folder && tmp[l-1]!='/')
+                strcat(tmp, "/");
+            PrependPath(tmp, collection, folder);
+        }
+    }
+
+}
+
+int FindInCollection(const char* path, path_collection_t* collection)
+{
+    for (int i=0; i<collection->size; ++i)
+        if(strcmp(path, collection->paths[i])==0)
+            return 1;
+    return 0;
+}
diff --git a/src/tools/wine_tools.c b/src/tools/wine_tools.c
new file mode 100755
index 00000000..6fa7a47d
--- /dev/null
+++ b/src/tools/wine_tools.c
@@ -0,0 +1,107 @@
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <sys/mman.h>
+#include <errno.h>
+
+#include "wine_tools.h"
+#include "debug.h"
+#include "box64context.h"
+
+typedef struct wine_prereserve_s
+{
+    void*   addr;
+    size_t  size;
+} wine_prereserve_t;
+
+// only the prereseve argument is reserved, not the other zone that wine-preloader reserve
+static wine_prereserve_t my_wine_reserve[] = {{(void*)0x00010000, 0x00008000}, {(void*)0x00110000, 0x30000000}, {(void*)0x7f000000, 0x03000000}, {0, 0}, {0, 0}};
+
+int wine_preloaded = 0;
+
+static int get_prereserve(const char* reserve, void** addr, size_t* size)
+{
+    if(!reserve)
+        return 0;
+    uintptr_t r = 0;
+    int first = 1;
+    while(*reserve) {
+        // numbers reading
+        if(*reserve>='0' && *reserve<='9')  r=r*16+(*reserve)-'0';
+        else if(*reserve>='A' && *reserve<='F')  r=r*16+(*reserve)-'A'+10;
+        else if(*reserve>='a' && *reserve<='f')  r=r*16+(*reserve)-'a'+10;
+        else if(*reserve=='-') {if(first) {*addr=(void*)(r&~(box64_pagesize-1)); r=0; first=0;} else {printf_log(LOG_NONE, "Warning, Wine prereserve badly formated\n"); return 0;}}
+        else {printf_log(LOG_NONE, "Warning, Wine prereserve badly formated\n"); return 0;}
+        ++reserve;
+    }
+    *size = r;
+    return 1;
+}
+
+static void add_no_overlap(void* addr, size_t size)
+{
+    int idx = 0;
+    while(my_wine_reserve[idx].addr && my_wine_reserve[idx].size) {
+        if(addr>=my_wine_reserve[idx].addr && addr<my_wine_reserve[idx].addr+my_wine_reserve[idx].size) {
+            // overlap
+            if (addr+size > my_wine_reserve[idx].addr+my_wine_reserve[idx].size)
+                // need adjust size
+                my_wine_reserve[idx].size = (intptr_t)addr-(intptr_t)my_wine_reserve[idx].addr+size;
+            return;
+        }
+        ++idx;
+    }
+    my_wine_reserve[idx].addr = addr;
+    my_wine_reserve[idx].size = size;
+}
+
+void wine_prereserve(const char* reserve)
+{
+    void* addr = NULL;
+    size_t size = 0;
+
+    if(get_prereserve(reserve, &addr, &size)) {
+        add_no_overlap(addr, size);
+    }
+
+    int idx = 0;
+    while(my_wine_reserve[idx].addr && my_wine_reserve[idx].size) {
+        void* p = mmap(my_wine_reserve[idx].addr, my_wine_reserve[idx].size, 
+                    PROT_NONE, /*MAP_FIXED |*/ MAP_PRIVATE | MAP_ANON | MAP_NORESERVE, -1, 0);
+        if(p==(void*)-1 || p!=my_wine_reserve[idx].addr) {
+            printf_log(LOG_NONE, "Warning, prereserve of %p:0x%x failed (%s)\n", my_wine_reserve[idx].addr, my_wine_reserve[idx].size, strerror(errno));
+            if(p!=(void*)-1)
+                munmap(p, my_wine_reserve[idx].size);
+            my_wine_reserve[idx].addr = NULL;
+            my_wine_reserve[idx].size = 0;
+        } else {
+            printf_log(LOG_DEBUG, "WINE prereserve of %p:0x%x done\n", my_wine_reserve[idx].addr, my_wine_reserve[idx].size);
+            ++idx;
+        }
+    }
+
+    wine_preloaded = 1;
+}
+
+void* get_wine_prereserve()
+{
+    if(!wine_preloaded)
+        wine_prereserve(NULL);
+    return (void*)my_wine_reserve;
+}
+
+#ifdef DYNAREC
+void dynarec_wine_prereserve()
+{
+    #if 0
+    // disable for now, as it break some installer
+    if(!wine_preloaded)
+        wine_prereserve(NULL);
+    // don't reserve the initial arbritrary block as "with linker", it's not true
+    for(int i=1; i<sizeof(my_wine_reserve)/sizeof(my_wine_reserve[0]); ++i)
+        if(my_wine_reserve[i].addr && my_wine_reserve[i].size)
+            addDBFromAddressRange(my_context, (uintptr_t)my_wine_reserve[i].addr, my_wine_reserve[i].size, 0);  // prepare the prereserved area for exec, with linker
+    #endif
+}
+#endif