From e05f137eeec4b6e8e127f164cf695a76a58c16ff Mon Sep 17 00:00:00 2001 From: doomertheboomer Date: Fri, 28 Jul 2023 14:51:42 +0700 Subject: [PATCH 1/7] create configuration program --- .gitignore | 6 +- depthrush.sln | 10 ++ depthrushConfig/calibration.cpp | 52 +++++++ depthrushConfig/calibration.h | 2 + depthrushConfig/depthrushConfig.cpp | 23 +++ depthrushConfig/depthrushConfig.vcxproj | 145 ++++++++++++++++++ .../depthrushConfig.vcxproj.filters | 33 ++++ depthrushConfig/includes.h | 7 + 8 files changed, 276 insertions(+), 2 deletions(-) create mode 100644 depthrushConfig/calibration.cpp create mode 100644 depthrushConfig/calibration.h create mode 100644 depthrushConfig/depthrushConfig.cpp create mode 100644 depthrushConfig/depthrushConfig.vcxproj create mode 100644 depthrushConfig/depthrushConfig.vcxproj.filters create mode 100644 depthrushConfig/includes.h diff --git a/.gitignore b/.gitignore index 33de78e..ee08d32 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,7 @@ .vs/ x64/ -depthrush/x64/ Release/ -depthrush/Release/ \ No newline at end of file +depthrush/x64/ +depthrush/Release/ +depthrushConfig/x64/ +depthrushConfig/Release/ \ No newline at end of file diff --git a/depthrush.sln b/depthrush.sln index 9cbc62f..10a9a64 100644 --- a/depthrush.sln +++ b/depthrush.sln @@ -5,6 +5,8 @@ VisualStudioVersion = 17.2.32616.157 MinimumVisualStudioVersion = 10.0.40219.1 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "depthrush", "depthrush\depthrush.vcxproj", "{04B17470-CB82-4724-904B-25445926AB86}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "depthrushConfig", "depthrushConfig\depthrushConfig.vcxproj", "{AF89F70A-7F60-4B1C-9532-CE7BBD02EA56}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|x64 = Debug|x64 @@ -21,6 +23,14 @@ Global {04B17470-CB82-4724-904B-25445926AB86}.Release|x64.Build.0 = Release|x64 {04B17470-CB82-4724-904B-25445926AB86}.Release|x86.ActiveCfg = Release|Win32 {04B17470-CB82-4724-904B-25445926AB86}.Release|x86.Build.0 = Release|Win32 + {AF89F70A-7F60-4B1C-9532-CE7BBD02EA56}.Debug|x64.ActiveCfg = Debug|x64 + {AF89F70A-7F60-4B1C-9532-CE7BBD02EA56}.Debug|x64.Build.0 = Debug|x64 + {AF89F70A-7F60-4B1C-9532-CE7BBD02EA56}.Debug|x86.ActiveCfg = Debug|Win32 + {AF89F70A-7F60-4B1C-9532-CE7BBD02EA56}.Debug|x86.Build.0 = Debug|Win32 + {AF89F70A-7F60-4B1C-9532-CE7BBD02EA56}.Release|x64.ActiveCfg = Release|x64 + {AF89F70A-7F60-4B1C-9532-CE7BBD02EA56}.Release|x64.Build.0 = Release|x64 + {AF89F70A-7F60-4B1C-9532-CE7BBD02EA56}.Release|x86.ActiveCfg = Release|Win32 + {AF89F70A-7F60-4B1C-9532-CE7BBD02EA56}.Release|x86.Build.0 = Release|Win32 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/depthrushConfig/calibration.cpp b/depthrushConfig/calibration.cpp new file mode 100644 index 0000000..3b53f59 --- /dev/null +++ b/depthrushConfig/calibration.cpp @@ -0,0 +1,52 @@ +#include "includes.h" +Vector4 leftLegPos = { 1.5F, 1.5F, 1.5F, 1.5F }; +Vector4 rightLegPos = { 1.5F, 1.5F, 1.5F, 1.5F }; + +int calibration() { + + // initialize Kinect + HRESULT hr = NuiInitialize(NUI_INITIALIZE_FLAG_USES_SKELETON); + if (FAILED(hr)) { + std::cout << "Failed to initialize Kinect." << std::endl; + return 1; + } + + // open the skeleton stream + HANDLE skeletonStream = nullptr; + hr = NuiSkeletonTrackingEnable(nullptr, 0); + if (FAILED(hr)) { + std::cout << "Failed to open the skeleton stream." << std::endl; + NuiShutdown(); + return 1; + } + + // start calibration application + std::thread calibrateMenu([] { + std::cout << std::to_string(leftLegPos.x); + }); + calibrateMenu.detach(); + + // main loop to read and process skeleton data + NUI_SKELETON_FRAME skeletonFrame = { 0 }; + while (true) { + // get the latest skeleton frame + hr = NuiSkeletonGetNextFrame(0, &skeletonFrame); + if (FAILED(hr)) { + continue; + } + + // Process each tracked skeleton + for (int i = 0; i < NUI_SKELETON_COUNT; ++i) { + if (skeletonFrame.SkeletonData[i].eTrackingState == NUI_SKELETON_TRACKED) { + // get the position of both legs + leftLegPos = skeletonFrame.SkeletonData[i].SkeletonPositions[NUI_SKELETON_POSITION_ANKLE_LEFT]; + rightLegPos = skeletonFrame.SkeletonData[i].SkeletonPositions[NUI_SKELETON_POSITION_ANKLE_RIGHT]; + } + } + } + + // Clean up and exit + NuiSkeletonTrackingDisable(); + NuiShutdown(); + return 0; +} \ No newline at end of file diff --git a/depthrushConfig/calibration.h b/depthrushConfig/calibration.h new file mode 100644 index 0000000..31423ba --- /dev/null +++ b/depthrushConfig/calibration.h @@ -0,0 +1,2 @@ +#pragma once +int calibration(); \ No newline at end of file diff --git a/depthrushConfig/depthrushConfig.cpp b/depthrushConfig/depthrushConfig.cpp new file mode 100644 index 0000000..d61cd54 --- /dev/null +++ b/depthrushConfig/depthrushConfig.cpp @@ -0,0 +1,23 @@ +#include "includes.h" + +int main() +{ + int choice = 0; + std::cout << "Depthrush test application\n"; + std::cout << "1. Calibrate Kinect\n"; + std::cout << "2. Preview Kinect\n"; + std::cout << "Enter a choice (1,2): "; + std::cin >> choice; + if (choice == 1) { + calibration(); + return 0; + } + else if (choice == 2) { + std::cout << "okay 2"; + return 0; + } + else { + std::cout << "Invalid option!"; + } + return 0; +} \ No newline at end of file diff --git a/depthrushConfig/depthrushConfig.vcxproj b/depthrushConfig/depthrushConfig.vcxproj new file mode 100644 index 0000000..bca088f --- /dev/null +++ b/depthrushConfig/depthrushConfig.vcxproj @@ -0,0 +1,145 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 16.0 + Win32Proj + {af89f70a-7f60-4b1c-9532-ce7bbd02ea56} + depthrushConfig + 10.0 + + + + Application + true + v143 + Unicode + + + Application + false + v143 + true + Unicode + + + Application + true + v143 + Unicode + + + Application + false + v143 + true + Unicode + + + + + + + + + + + + + + + + + + + + + C:\Program Files\Microsoft SDKs\Kinect\v1.7\inc;$(IncludePath) + C:\Program Files\Microsoft SDKs\Kinect\v1.7\lib\amd64;$(LibraryPath) + + + + Level3 + true + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + + + + + Level3 + true + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + true + true + + + + + Level3 + true + _DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + + + + + Level3 + true + true + true + NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + true + true + Kinect10.lib;%(AdditionalDependencies) + + + + + + + + + + + + + + \ No newline at end of file diff --git a/depthrushConfig/depthrushConfig.vcxproj.filters b/depthrushConfig/depthrushConfig.vcxproj.filters new file mode 100644 index 0000000..6a20e07 --- /dev/null +++ b/depthrushConfig/depthrushConfig.vcxproj.filters @@ -0,0 +1,33 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Source Files + + + Source Files + + + + + Header Files + + + Header Files + + + \ No newline at end of file diff --git a/depthrushConfig/includes.h b/depthrushConfig/includes.h new file mode 100644 index 0000000..7b5daaf --- /dev/null +++ b/depthrushConfig/includes.h @@ -0,0 +1,7 @@ +#pragma once +#include +#include +#include +#include +#include +#include "calibration.h" \ No newline at end of file From 423ef98ec96de2a4b33c2fee338239cdafaeb074 Mon Sep 17 00:00:00 2001 From: doomertheboomer Date: Fri, 28 Jul 2023 16:55:31 +0700 Subject: [PATCH 2/7] write calibration code --- depthrushConfig/calibration.cpp | 36 ++++++++++++++++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) diff --git a/depthrushConfig/calibration.cpp b/depthrushConfig/calibration.cpp index 3b53f59..cc98065 100644 --- a/depthrushConfig/calibration.cpp +++ b/depthrushConfig/calibration.cpp @@ -22,8 +22,42 @@ int calibration() { // start calibration application std::thread calibrateMenu([] { - std::cout << std::to_string(leftLegPos.x); + std::cout << "Starting calibration... Make sure your kinect is pointed straight ahead at you!\n"; + + // X calibration + std::cout << "Please place your left foot on the left side of the pad for 5 seconds.\n"; + Sleep(5000); + float xMin = leftLegPos.x; + std::cout << "Left side recorded.\n"; + std::cout << "Please place your right foot on the right side of the pad for 5 seconds.\n"; + Sleep(5000); + float xMax = rightLegPos.x; + std::cout << "Right side recorded.\n"; + float xGrad = (1 - 0) / (xMax - xMin); + float xOffset = -(xGrad * xMin); + + // Y and Z calibration + + // Z calibration + std::cout << "Please place your left foot at the front of the pad for 5 seconds.\n"; + Sleep(5000); + float zMin = leftLegPos.z; + float yMin = leftLegPos.y; + std::cout << "Front recorded.\n"; + std::cout << "Please place your left foot at the back of the pad for 5 seconds.\n"; + Sleep(5000); + float zMax = leftLegPos.z; + float yMax = leftLegPos.y; + std::cout << "Back recorded.\n"; + float zGrad = (1 - 0) / (zMax - zMin); + float zOffset = -(zGrad * zMin); + + //Y calibration + float yGrad = (yMax - yMin) / (zMax - zMin); + float yOffset = (yMin - yGrad * zMin); }); + + calibrateMenu.detach(); // main loop to read and process skeleton data From 76e688b6b221ebbeb68b4d0109e90379e8648e46 Mon Sep 17 00:00:00 2001 From: doomertheboomer Date: Fri, 28 Jul 2023 17:56:16 +0700 Subject: [PATCH 3/7] write preview code, calibration code is flawed for now --- depthrushConfig/calibration.cpp | 16 ++- depthrushConfig/depthrushConfig.cpp | 2 +- depthrushConfig/depthrushConfig.vcxproj | 2 + .../depthrushConfig.vcxproj.filters | 6 + depthrushConfig/depthrushConfig.vcxproj.user | 4 + depthrushConfig/includes.h | 5 +- depthrushConfig/kinectTest.cpp | 111 ++++++++++++++++++ depthrushConfig/kinectTest.h | 2 + 8 files changed, 145 insertions(+), 3 deletions(-) create mode 100644 depthrushConfig/depthrushConfig.vcxproj.user create mode 100644 depthrushConfig/kinectTest.cpp create mode 100644 depthrushConfig/kinectTest.h diff --git a/depthrushConfig/calibration.cpp b/depthrushConfig/calibration.cpp index cc98065..c2fa0ef 100644 --- a/depthrushConfig/calibration.cpp +++ b/depthrushConfig/calibration.cpp @@ -1,6 +1,7 @@ #include "includes.h" Vector4 leftLegPos = { 1.5F, 1.5F, 1.5F, 1.5F }; Vector4 rightLegPos = { 1.5F, 1.5F, 1.5F, 1.5F }; +bool kinectScanning = true; int calibration() { @@ -55,6 +56,18 @@ int calibration() { //Y calibration float yGrad = (yMax - yMin) / (zMax - zMin); float yOffset = (yMin - yGrad * zMin); + + + // Clean up and proceed to kinect test + kinectScanning = false; + std::cout << std::to_string(xGrad) << " "; + std::cout << std::to_string(xOffset) << " "; + std::cout << std::to_string(yGrad) << " "; + std::cout << std::to_string(yOffset) << " "; + std::cout << std::to_string(zGrad) << " "; + std::cout << std::to_string(zOffset) << "\n"; + std::cout << "Proceeding to Kinect Preview..\n"; + kinectTest(xGrad, xOffset, yGrad, yOffset, zGrad, zOffset); }); @@ -62,7 +75,7 @@ int calibration() { // main loop to read and process skeleton data NUI_SKELETON_FRAME skeletonFrame = { 0 }; - while (true) { + while (kinectScanning) { // get the latest skeleton frame hr = NuiSkeletonGetNextFrame(0, &skeletonFrame); if (FAILED(hr)) { @@ -82,5 +95,6 @@ int calibration() { // Clean up and exit NuiSkeletonTrackingDisable(); NuiShutdown(); + std::this_thread::sleep_for(std::chrono::hours(1000)); return 0; } \ No newline at end of file diff --git a/depthrushConfig/depthrushConfig.cpp b/depthrushConfig/depthrushConfig.cpp index d61cd54..917944f 100644 --- a/depthrushConfig/depthrushConfig.cpp +++ b/depthrushConfig/depthrushConfig.cpp @@ -13,7 +13,7 @@ int main() return 0; } else if (choice == 2) { - std::cout << "okay 2"; + kinectTest(1, 1, 1, 1, 1, 1); return 0; } else { diff --git a/depthrushConfig/depthrushConfig.vcxproj b/depthrushConfig/depthrushConfig.vcxproj index bca088f..ecb007a 100644 --- a/depthrushConfig/depthrushConfig.vcxproj +++ b/depthrushConfig/depthrushConfig.vcxproj @@ -134,10 +134,12 @@ + + diff --git a/depthrushConfig/depthrushConfig.vcxproj.filters b/depthrushConfig/depthrushConfig.vcxproj.filters index 6a20e07..917980d 100644 --- a/depthrushConfig/depthrushConfig.vcxproj.filters +++ b/depthrushConfig/depthrushConfig.vcxproj.filters @@ -21,6 +21,9 @@ Source Files + + Source Files + @@ -29,5 +32,8 @@ Header Files + + Header Files + \ No newline at end of file diff --git a/depthrushConfig/depthrushConfig.vcxproj.user b/depthrushConfig/depthrushConfig.vcxproj.user new file mode 100644 index 0000000..88a5509 --- /dev/null +++ b/depthrushConfig/depthrushConfig.vcxproj.user @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/depthrushConfig/includes.h b/depthrushConfig/includes.h index 7b5daaf..a699c4d 100644 --- a/depthrushConfig/includes.h +++ b/depthrushConfig/includes.h @@ -4,4 +4,7 @@ #include #include #include -#include "calibration.h" \ No newline at end of file +#include "calibration.h" +#include "kinectTest.h" + + diff --git a/depthrushConfig/kinectTest.cpp b/depthrushConfig/kinectTest.cpp new file mode 100644 index 0000000..223d984 --- /dev/null +++ b/depthrushConfig/kinectTest.cpp @@ -0,0 +1,111 @@ +#include "includes.h" +Vector4 leftLegPosPreview = { 1.5F, 1.5F, 1.5F, 1.5F }; +Vector4 rightLegPosPreview = { 1.5F, 1.5F, 1.5F, 1.5F }; + +int kinectTest(float xGrad, float xOffset, float yGrad, float yOffset, float zGrad, float zOffset) { + //std::cout << "Kinect Preview\n"; + //std::cin >> xGrad; + //std::cin >> xOffset; + // Wait 10 secs for kinect shutdown + // Sleep(10000); + // initialize Kinect + HRESULT hr = NuiInitialize(NUI_INITIALIZE_FLAG_USES_SKELETON); + if (FAILED(hr)) { + std::cout << "Failed to initialize Kinect." << std::endl; + return 1; + } + + // open the skeleton stream + HANDLE skeletonStream = nullptr; + hr = NuiSkeletonTrackingEnable(nullptr, 0); + if (FAILED(hr)) { + std::cout << "Failed to open the skeleton stream." << std::endl; + NuiShutdown(); + return 1; + } + + // main loop to read and process skeleton data + NUI_SKELETON_FRAME skeletonFrame = { 0 }; + CONSOLE_SCREEN_BUFFER_INFO csbi; + int width = 0; + float drsLeft = 0; + float drsRight = 0; + int toolLeft = 0; + int toolRight = 0; + int toolWidth = 0; + bool leftTouch = false; + bool rightTouch = false; + + while (true) { + // get the latest skeleton frame + hr = NuiSkeletonGetNextFrame(0, &skeletonFrame); + if (FAILED(hr)) { + continue; + } + + // Process each tracked skeleton + for (int i = 0; i < NUI_SKELETON_COUNT; ++i) { + if (skeletonFrame.SkeletonData[i].eTrackingState == NUI_SKELETON_TRACKED) { + // get the position of both legs + leftLegPosPreview = skeletonFrame.SkeletonData[i].SkeletonPositions[NUI_SKELETON_POSITION_ANKLE_LEFT]; + rightLegPosPreview = skeletonFrame.SkeletonData[i].SkeletonPositions[NUI_SKELETON_POSITION_ANKLE_RIGHT]; + } + } + + // keep updating screen width for fancy + GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi); + width = (int)(csbi.srWindow.Right - csbi.srWindow.Left + 1); + + // fetch calibrated feet + drsLeft = xGrad * leftLegPosPreview.x + xOffset; + drsRight = xGrad * rightLegPosPreview.x + xOffset; + + // translate to console window width + toolLeft = width * drsLeft; + toolRight = width * drsRight; + toolWidth = width * 0.05; + + // foot lifting detection + // see how far foot currently is from known ground value + float fixedLeft = leftLegPosPreview.y - (yGrad * leftLegPosPreview.z + yOffset); + float fixedRight = rightLegPosPreview.y - (yGrad * rightLegPosPreview.z + yOffset); + + // check for stepping + float errorMargin = 0.05; + if (fixedLeft > (fixedRight + errorMargin)) { + rightTouch = true; + leftTouch = false; + // std::cout << "right step\n"; + } + else if (fixedLeft > (fixedRight + errorMargin)) { + leftTouch = true; + rightTouch = false; + // std::cout << "left step \n"; + } + else { + leftTouch = true; + rightTouch = true; + // std::cout << "both step\n"; + } + /* + // print feet + for (int i = 0; i < width; i++) { + if ((i <= (toolLeft + toolWidth)) && (i >= (toolLeft - toolWidth)) && leftTouch) { + std::cout << "L"; + } + else if ((i <= (toolRight + toolWidth)) && (i >= (toolRight - toolWidth)) && rightTouch) { + std::cout << "R"; + } + else { + std::cout << " "; + } + } + */ + std::cout << std::to_string(fixedLeft) << " " << std::to_string(fixedRight) << std::endl; + } + + // Clean up and exit + NuiSkeletonTrackingDisable(); + NuiShutdown(); + return 0; +} \ No newline at end of file diff --git a/depthrushConfig/kinectTest.h b/depthrushConfig/kinectTest.h new file mode 100644 index 0000000..22da6ab --- /dev/null +++ b/depthrushConfig/kinectTest.h @@ -0,0 +1,2 @@ +#pragma once +int kinectTest(float xGrad, float xOffset, float yGrad, float yOffset, float zGrad, float zOffset); \ No newline at end of file From f01375f72037f0cad56ba10f5539f04a9f1f99e2 Mon Sep 17 00:00:00 2001 From: doomertheboomer Date: Fri, 28 Jul 2023 18:07:12 +0700 Subject: [PATCH 4/7] fix preview code, calibration code was fine --- depthrushConfig/kinectTest.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/depthrushConfig/kinectTest.cpp b/depthrushConfig/kinectTest.cpp index 223d984..7570543 100644 --- a/depthrushConfig/kinectTest.cpp +++ b/depthrushConfig/kinectTest.cpp @@ -72,12 +72,13 @@ int kinectTest(float xGrad, float xOffset, float yGrad, float yOffset, float zGr // check for stepping float errorMargin = 0.05; + if (fixedLeft > (fixedRight + errorMargin)) { rightTouch = true; leftTouch = false; // std::cout << "right step\n"; } - else if (fixedLeft > (fixedRight + errorMargin)) { + else if (fixedRight > (fixedLeft + errorMargin)) { leftTouch = true; rightTouch = false; // std::cout << "left step \n"; @@ -87,7 +88,7 @@ int kinectTest(float xGrad, float xOffset, float yGrad, float yOffset, float zGr rightTouch = true; // std::cout << "both step\n"; } - /* + // print feet for (int i = 0; i < width; i++) { if ((i <= (toolLeft + toolWidth)) && (i >= (toolLeft - toolWidth)) && leftTouch) { @@ -100,8 +101,8 @@ int kinectTest(float xGrad, float xOffset, float yGrad, float yOffset, float zGr std::cout << " "; } } - */ - std::cout << std::to_string(fixedLeft) << " " << std::to_string(fixedRight) << std::endl; + + // std::cout << std::to_string(fixedLeft) << " " << std::to_string(fixedRight) << std::endl; } // Clean up and exit From 40fc590106c7c1c4e0467f8565e6239d6e427605 Mon Sep 17 00:00:00 2001 From: doomertheboomer Date: Fri, 28 Jul 2023 19:13:32 +0700 Subject: [PATCH 5/7] add calibration saving --- depthrush/depthrush.vcxproj | 1 + depthrush/depthrush.vcxproj.filters | 3 + depthrush/includes.h | 1 + depthrush/ini.h | 789 ++++++++++++++++++ depthrushConfig/calibration.cpp | 14 + depthrushConfig/depthrushConfig.cpp | 19 +- depthrushConfig/depthrushConfig.vcxproj | 1 + .../depthrushConfig.vcxproj.filters | 3 + depthrushConfig/includes.h | 1 + depthrushConfig/ini.h | 789 ++++++++++++++++++ 10 files changed, 1620 insertions(+), 1 deletion(-) create mode 100644 depthrush/ini.h create mode 100644 depthrushConfig/ini.h diff --git a/depthrush/depthrush.vcxproj b/depthrush/depthrush.vcxproj index af97827..9348b74 100644 --- a/depthrush/depthrush.vcxproj +++ b/depthrush/depthrush.vcxproj @@ -171,6 +171,7 @@ + diff --git a/depthrush/depthrush.vcxproj.filters b/depthrush/depthrush.vcxproj.filters index e55c07d..6324a0e 100644 --- a/depthrush/depthrush.vcxproj.filters +++ b/depthrush/depthrush.vcxproj.filters @@ -103,6 +103,9 @@ Header Files + + Header Files + diff --git a/depthrush/includes.h b/depthrush/includes.h index 94fb7b9..63d8c55 100644 --- a/depthrush/includes.h +++ b/depthrush/includes.h @@ -9,6 +9,7 @@ #include "kiero/minhook/include/MinHook.h" #include "kiero/injector/injector.hpp" #include "d3d9.h" +#include "ini.h" #include "drs.h" typedef LRESULT(CALLBACK* WNDPROC)(HWND, UINT, WPARAM, LPARAM); diff --git a/depthrush/ini.h b/depthrush/ini.h new file mode 100644 index 0000000..cb0b801 --- /dev/null +++ b/depthrush/ini.h @@ -0,0 +1,789 @@ +/* + * The MIT License (MIT) + * Copyright (c) 2018 Danijel Durakovic + * + * 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. + * + */ + + /////////////////////////////////////////////////////////////////////////////// + // + // /mINI/ v0.9.14 + // An INI file reader and writer for the modern age. + // + /////////////////////////////////////////////////////////////////////////////// + // + // A tiny utility library for manipulating INI files with a straightforward + // API and a minimal footprint. It conforms to the (somewhat) standard INI + // format - sections and keys are case insensitive and all leading and + // trailing whitespace is ignored. Comments are lines that begin with a + // semicolon. Trailing comments are allowed on section lines. + // + // Files are read on demand, upon which data is kept in memory and the file + // is closed. This utility supports lazy writing, which only writes changes + // and updates to a file and preserves custom formatting and comments. A lazy + // write invoked by a write() call will read the output file, find what + // changes have been made and update the file accordingly. If you only need to + // generate files, use generate() instead. Section and key order is preserved + // on read, write and insert. + // + /////////////////////////////////////////////////////////////////////////////// + // + // /* BASIC USAGE EXAMPLE: */ + // + // /* read from file */ + // mINI::INIFile file("myfile.ini"); + // mINI::INIStructure ini; + // file.read(ini); + // + // /* read value; gets a reference to actual value in the structure. + // if key or section don't exist, a new empty value will be created */ + // std::string& value = ini["section"]["key"]; + // + // /* read value safely; gets a copy of value in the structure. + // does not alter the structure */ + // std::string value = ini.get("section").get("key"); + // + // /* set or update values */ + // ini["section"]["key"] = "value"; + // + // /* set multiple values */ + // ini["section2"].set({ + // {"key1", "value1"}, + // {"key2", "value2"} + // }); + // + // /* write updates back to file, preserving comments and formatting */ + // file.write(ini); + // + // /* or generate a file (overwrites the original) */ + // file.generate(ini); + // + /////////////////////////////////////////////////////////////////////////////// + // + // Long live the INI file!!! + // + /////////////////////////////////////////////////////////////////////////////// + +#ifndef MINI_INI_H_ +#define MINI_INI_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace mINI +{ + namespace INIStringUtil + { + const char* const whitespaceDelimiters = " \t\n\r\f\v"; + inline void trim(std::string& str) + { + str.erase(str.find_last_not_of(whitespaceDelimiters) + 1); + str.erase(0, str.find_first_not_of(whitespaceDelimiters)); + } +#ifndef MINI_CASE_SENSITIVE + inline void toLower(std::string& str) + { + std::transform(str.begin(), str.end(), str.begin(), [](const char c) { + return static_cast(std::tolower(c)); + }); + } +#endif + inline void replace(std::string& str, std::string const& a, std::string const& b) + { + if (!a.empty()) + { + std::size_t pos = 0; + while ((pos = str.find(a, pos)) != std::string::npos) + { + str.replace(pos, a.size(), b); + pos += b.size(); + } + } + } +#ifdef _WIN32 + const char* const endl = "\r\n"; +#else + const char* const endl = "\n"; +#endif + } + + template + class INIMap + { + private: + using T_DataIndexMap = std::unordered_map; + using T_DataItem = std::pair; + using T_DataContainer = std::vector; + using T_MultiArgs = typename std::vector>; + + T_DataIndexMap dataIndexMap; + T_DataContainer data; + + inline std::size_t setEmpty(std::string& key) + { + std::size_t index = data.size(); + dataIndexMap[key] = index; + data.emplace_back(key, T()); + return index; + } + + public: + using const_iterator = typename T_DataContainer::const_iterator; + + INIMap() { } + + INIMap(INIMap const& other) + { + std::size_t data_size = other.data.size(); + for (std::size_t i = 0; i < data_size; ++i) + { + auto const& key = other.data[i].first; + auto const& obj = other.data[i].second; + data.emplace_back(key, obj); + } + dataIndexMap = T_DataIndexMap(other.dataIndexMap); + } + + T& operator[](std::string key) + { + INIStringUtil::trim(key); +#ifndef MINI_CASE_SENSITIVE + INIStringUtil::toLower(key); +#endif + auto it = dataIndexMap.find(key); + bool hasIt = (it != dataIndexMap.end()); + std::size_t index = (hasIt) ? it->second : setEmpty(key); + return data[index].second; + } + T get(std::string key) const + { + INIStringUtil::trim(key); +#ifndef MINI_CASE_SENSITIVE + INIStringUtil::toLower(key); +#endif + auto it = dataIndexMap.find(key); + if (it == dataIndexMap.end()) + { + return T(); + } + return T(data[it->second].second); + } + bool has(std::string key) const + { + INIStringUtil::trim(key); +#ifndef MINI_CASE_SENSITIVE + INIStringUtil::toLower(key); +#endif + return (dataIndexMap.count(key) == 1); + } + void set(std::string key, T obj) + { + INIStringUtil::trim(key); +#ifndef MINI_CASE_SENSITIVE + INIStringUtil::toLower(key); +#endif + auto it = dataIndexMap.find(key); + if (it != dataIndexMap.end()) + { + data[it->second].second = obj; + } + else + { + dataIndexMap[key] = data.size(); + data.emplace_back(key, obj); + } + } + void set(T_MultiArgs const& multiArgs) + { + for (auto const& it : multiArgs) + { + auto const& key = it.first; + auto const& obj = it.second; + set(key, obj); + } + } + bool remove(std::string key) + { + INIStringUtil::trim(key); +#ifndef MINI_CASE_SENSITIVE + INIStringUtil::toLower(key); +#endif + auto it = dataIndexMap.find(key); + if (it != dataIndexMap.end()) + { + std::size_t index = it->second; + data.erase(data.begin() + index); + dataIndexMap.erase(it); + for (auto& it2 : dataIndexMap) + { + auto& vi = it2.second; + if (vi > index) + { + vi--; + } + } + return true; + } + return false; + } + void clear() + { + data.clear(); + dataIndexMap.clear(); + } + std::size_t size() const + { + return data.size(); + } + const_iterator begin() const { return data.begin(); } + const_iterator end() const { return data.end(); } + }; + + using INIStructure = INIMap>; + + namespace INIParser + { + using T_ParseValues = std::pair; + + enum class PDataType : char + { + PDATA_NONE, + PDATA_COMMENT, + PDATA_SECTION, + PDATA_KEYVALUE, + PDATA_UNKNOWN + }; + + inline PDataType parseLine(std::string line, T_ParseValues& parseData) + { + parseData.first.clear(); + parseData.second.clear(); + INIStringUtil::trim(line); + if (line.empty()) + { + return PDataType::PDATA_NONE; + } + char firstCharacter = line[0]; + if (firstCharacter == ';') + { + return PDataType::PDATA_COMMENT; + } + if (firstCharacter == '[') + { + auto commentAt = line.find_first_of(';'); + if (commentAt != std::string::npos) + { + line = line.substr(0, commentAt); + } + auto closingBracketAt = line.find_last_of(']'); + if (closingBracketAt != std::string::npos) + { + auto section = line.substr(1, closingBracketAt - 1); + INIStringUtil::trim(section); + parseData.first = section; + return PDataType::PDATA_SECTION; + } + } + auto lineNorm = line; + INIStringUtil::replace(lineNorm, "\\=", " "); + auto equalsAt = lineNorm.find_first_of('='); + if (equalsAt != std::string::npos) + { + auto key = line.substr(0, equalsAt); + INIStringUtil::trim(key); + INIStringUtil::replace(key, "\\=", "="); + auto value = line.substr(equalsAt + 1); + INIStringUtil::trim(value); + parseData.first = key; + parseData.second = value; + return PDataType::PDATA_KEYVALUE; + } + return PDataType::PDATA_UNKNOWN; + } + } + + class INIReader + { + public: + using T_LineData = std::vector; + using T_LineDataPtr = std::shared_ptr; + + bool isBOM = false; + + private: + std::ifstream fileReadStream; + T_LineDataPtr lineData; + + T_LineData readFile() + { + fileReadStream.seekg(0, std::ios::end); + const std::size_t fileSize = static_cast(fileReadStream.tellg()); + fileReadStream.seekg(0, std::ios::beg); + if (fileSize >= 3) { + const char header[3] = { + static_cast(fileReadStream.get()), + static_cast(fileReadStream.get()), + static_cast(fileReadStream.get()) + }; + isBOM = ( + header[0] == static_cast(0xEF) && + header[1] == static_cast(0xBB) && + header[2] == static_cast(0xBF) + ); + } + else { + isBOM = false; + } + std::string fileContents; + fileContents.resize(fileSize); + fileReadStream.seekg(isBOM ? 3 : 0, std::ios::beg); + fileReadStream.read(&fileContents[0], fileSize); + fileReadStream.close(); + T_LineData output; + if (fileSize == 0) + { + return output; + } + std::string buffer; + buffer.reserve(50); + for (std::size_t i = 0; i < fileSize; ++i) + { + char& c = fileContents[i]; + if (c == '\n') + { + output.emplace_back(buffer); + buffer.clear(); + continue; + } + if (c != '\0' && c != '\r') + { + buffer += c; + } + } + output.emplace_back(buffer); + return output; + } + + public: + INIReader(std::string const& filename, bool keepLineData = false) + { + fileReadStream.open(filename, std::ios::in | std::ios::binary); + if (keepLineData) + { + lineData = std::make_shared(); + } + } + ~INIReader() { } + + bool operator>>(INIStructure& data) + { + if (!fileReadStream.is_open()) + { + return false; + } + T_LineData fileLines = readFile(); + std::string section; + bool inSection = false; + INIParser::T_ParseValues parseData; + for (auto const& line : fileLines) + { + auto parseResult = INIParser::parseLine(line, parseData); + if (parseResult == INIParser::PDataType::PDATA_SECTION) + { + inSection = true; + data[section = parseData.first]; + } + else if (inSection && parseResult == INIParser::PDataType::PDATA_KEYVALUE) + { + auto const& key = parseData.first; + auto const& value = parseData.second; + data[section][key] = value; + } + if (lineData && parseResult != INIParser::PDataType::PDATA_UNKNOWN) + { + if (parseResult == INIParser::PDataType::PDATA_KEYVALUE && !inSection) + { + continue; + } + lineData->emplace_back(line); + } + } + return true; + } + T_LineDataPtr getLines() + { + return lineData; + } + }; + + class INIGenerator + { + private: + std::ofstream fileWriteStream; + + public: + bool prettyPrint = false; + + INIGenerator(std::string const& filename) + { + fileWriteStream.open(filename, std::ios::out | std::ios::binary); + } + ~INIGenerator() { } + + bool operator<<(INIStructure const& data) + { + if (!fileWriteStream.is_open()) + { + return false; + } + if (!data.size()) + { + return true; + } + auto it = data.begin(); + for (;;) + { + auto const& section = it->first; + auto const& collection = it->second; + fileWriteStream + << "[" + << section + << "]"; + if (collection.size()) + { + fileWriteStream << INIStringUtil::endl; + auto it2 = collection.begin(); + for (;;) + { + auto key = it2->first; + INIStringUtil::replace(key, "=", "\\="); + auto value = it2->second; + INIStringUtil::trim(value); + fileWriteStream + << key + << ((prettyPrint) ? " = " : "=") + << value; + if (++it2 == collection.end()) + { + break; + } + fileWriteStream << INIStringUtil::endl; + } + } + if (++it == data.end()) + { + break; + } + fileWriteStream << INIStringUtil::endl; + if (prettyPrint) + { + fileWriteStream << INIStringUtil::endl; + } + } + return true; + } + }; + + class INIWriter + { + private: + using T_LineData = std::vector; + using T_LineDataPtr = std::shared_ptr; + + std::string filename; + + T_LineData getLazyOutput(T_LineDataPtr const& lineData, INIStructure& data, INIStructure& original) + { + T_LineData output; + INIParser::T_ParseValues parseData; + std::string sectionCurrent; + bool parsingSection = false; + bool continueToNextSection = false; + bool discardNextEmpty = false; + bool writeNewKeys = false; + std::size_t lastKeyLine = 0; + for (auto line = lineData->begin(); line != lineData->end(); ++line) + { + if (!writeNewKeys) + { + auto parseResult = INIParser::parseLine(*line, parseData); + if (parseResult == INIParser::PDataType::PDATA_SECTION) + { + if (parsingSection) + { + writeNewKeys = true; + parsingSection = false; + --line; + continue; + } + sectionCurrent = parseData.first; + if (data.has(sectionCurrent)) + { + parsingSection = true; + continueToNextSection = false; + discardNextEmpty = false; + output.emplace_back(*line); + lastKeyLine = output.size(); + } + else + { + continueToNextSection = true; + discardNextEmpty = true; + continue; + } + } + else if (parseResult == INIParser::PDataType::PDATA_KEYVALUE) + { + if (continueToNextSection) + { + continue; + } + if (data.has(sectionCurrent)) + { + auto& collection = data[sectionCurrent]; + auto const& key = parseData.first; + auto const& value = parseData.second; + if (collection.has(key)) + { + auto outputValue = collection[key]; + if (value == outputValue) + { + output.emplace_back(*line); + } + else + { + INIStringUtil::trim(outputValue); + auto lineNorm = *line; + INIStringUtil::replace(lineNorm, "\\=", " "); + auto equalsAt = lineNorm.find_first_of('='); + auto valueAt = lineNorm.find_first_not_of( + INIStringUtil::whitespaceDelimiters, + equalsAt + 1 + ); + std::string outputLine = line->substr(0, valueAt); + if (prettyPrint && equalsAt + 1 == valueAt) + { + outputLine += " "; + } + outputLine += outputValue; + output.emplace_back(outputLine); + } + lastKeyLine = output.size(); + } + } + } + else + { + if (discardNextEmpty && line->empty()) + { + discardNextEmpty = false; + } + else if (parseResult != INIParser::PDataType::PDATA_UNKNOWN) + { + output.emplace_back(*line); + } + } + } + if (writeNewKeys || std::next(line) == lineData->end()) + { + T_LineData linesToAdd; + if (data.has(sectionCurrent) && original.has(sectionCurrent)) + { + auto const& collection = data[sectionCurrent]; + auto const& collectionOriginal = original[sectionCurrent]; + for (auto const& it : collection) + { + auto key = it.first; + if (collectionOriginal.has(key)) + { + continue; + } + auto value = it.second; + INIStringUtil::replace(key, "=", "\\="); + INIStringUtil::trim(value); + linesToAdd.emplace_back( + key + ((prettyPrint) ? " = " : "=") + value + ); + } + } + if (!linesToAdd.empty()) + { + output.insert( + output.begin() + lastKeyLine, + linesToAdd.begin(), + linesToAdd.end() + ); + } + if (writeNewKeys) + { + writeNewKeys = false; + --line; + } + } + } + for (auto const& it : data) + { + auto const& section = it.first; + if (original.has(section)) + { + continue; + } + if (prettyPrint && output.size() > 0 && !output.back().empty()) + { + output.emplace_back(); + } + output.emplace_back("[" + section + "]"); + auto const& collection = it.second; + for (auto const& it2 : collection) + { + auto key = it2.first; + auto value = it2.second; + INIStringUtil::replace(key, "=", "\\="); + INIStringUtil::trim(value); + output.emplace_back( + key + ((prettyPrint) ? " = " : "=") + value + ); + } + } + return output; + } + + public: + bool prettyPrint = false; + + INIWriter(std::string const& filename) + : filename(filename) + { + } + ~INIWriter() { } + + bool operator<<(INIStructure& data) + { + struct stat buf; + bool fileExists = (stat(filename.c_str(), &buf) == 0); + if (!fileExists) + { + INIGenerator generator(filename); + generator.prettyPrint = prettyPrint; + return generator << data; + } + INIStructure originalData; + T_LineDataPtr lineData; + bool readSuccess = false; + bool fileIsBOM = false; + { + INIReader reader(filename, true); + if ((readSuccess = reader >> originalData)) + { + lineData = reader.getLines(); + fileIsBOM = reader.isBOM; + } + } + if (!readSuccess) + { + return false; + } + T_LineData output = getLazyOutput(lineData, data, originalData); + std::ofstream fileWriteStream(filename, std::ios::out | std::ios::binary); + if (fileWriteStream.is_open()) + { + if (fileIsBOM) { + const char utf8_BOM[3] = { + static_cast(0xEF), + static_cast(0xBB), + static_cast(0xBF) + }; + fileWriteStream.write(utf8_BOM, 3); + } + if (output.size()) + { + auto line = output.begin(); + for (;;) + { + fileWriteStream << *line; + if (++line == output.end()) + { + break; + } + fileWriteStream << INIStringUtil::endl; + } + } + return true; + } + return false; + } + }; + + class INIFile + { + private: + std::string filename; + + public: + INIFile(std::string const& filename) + : filename(filename) + { } + + ~INIFile() { } + + bool read(INIStructure& data) const + { + if (data.size()) + { + data.clear(); + } + if (filename.empty()) + { + return false; + } + INIReader reader(filename); + return reader >> data; + } + bool generate(INIStructure const& data, bool pretty = false) const + { + if (filename.empty()) + { + return false; + } + INIGenerator generator(filename); + generator.prettyPrint = pretty; + return generator << data; + } + bool write(INIStructure& data, bool pretty = false) const + { + if (filename.empty()) + { + return false; + } + INIWriter writer(filename); + writer.prettyPrint = pretty; + return writer << data; + } + }; +} + +#endif // MINI_INI_H_ \ No newline at end of file diff --git a/depthrushConfig/calibration.cpp b/depthrushConfig/calibration.cpp index c2fa0ef..7caaa47 100644 --- a/depthrushConfig/calibration.cpp +++ b/depthrushConfig/calibration.cpp @@ -60,12 +60,26 @@ int calibration() { // Clean up and proceed to kinect test kinectScanning = false; + std::cout << "Saving the following values to depthrush.ini...\n"; std::cout << std::to_string(xGrad) << " "; std::cout << std::to_string(xOffset) << " "; std::cout << std::to_string(yGrad) << " "; std::cout << std::to_string(yOffset) << " "; std::cout << std::to_string(zGrad) << " "; std::cout << std::to_string(zOffset) << "\n"; + + // Save values + mINI::INIFile file("depthrush.ini"); + mINI::INIStructure ini; + ini["calibration"]["xGrad"] = std::to_string(xGrad); + ini["calibration"]["xOffset"] = std::to_string(xOffset); + ini["calibration"]["yGrad"] = std::to_string(yGrad); + ini["calibration"]["yOffset"] = std::to_string(yOffset); + ini["calibration"]["zGrad"] = std::to_string(zGrad); + ini["calibration"]["zOffset"] = std::to_string(zOffset); + file.generate(ini); + std::cout << "Save complete.\n"; + std::cout << "Proceeding to Kinect Preview..\n"; kinectTest(xGrad, xOffset, yGrad, yOffset, zGrad, zOffset); }); diff --git a/depthrushConfig/depthrushConfig.cpp b/depthrushConfig/depthrushConfig.cpp index 917944f..15b9474 100644 --- a/depthrushConfig/depthrushConfig.cpp +++ b/depthrushConfig/depthrushConfig.cpp @@ -2,6 +2,23 @@ int main() { + // load config from ini + mINI::INIFile file("depthrush.ini"); + mINI::INIStructure ini; + file.read(ini); + std::string& readValue = ini["calibration"]["xGrad"]; + float xGrad = std::stof(readValue); + readValue = ini["calibration"]["xOffset"]; + float xOffset = std::stof(readValue); + readValue = ini["calibration"]["yGrad"]; + float yGrad = std::stof(readValue); + readValue = ini["calibration"]["yOffset"]; + float yOffset = std::stof(readValue); + readValue = ini["calibration"]["zGrad"]; + float zGrad = std::stof(readValue); + readValue = ini["calibration"]["zOffset"]; + float zOffset = std::stof(readValue); + int choice = 0; std::cout << "Depthrush test application\n"; std::cout << "1. Calibrate Kinect\n"; @@ -13,7 +30,7 @@ int main() return 0; } else if (choice == 2) { - kinectTest(1, 1, 1, 1, 1, 1); + kinectTest(xGrad, xOffset, yGrad, yOffset, zGrad, zOffset); return 0; } else { diff --git a/depthrushConfig/depthrushConfig.vcxproj b/depthrushConfig/depthrushConfig.vcxproj index ecb007a..0116fe4 100644 --- a/depthrushConfig/depthrushConfig.vcxproj +++ b/depthrushConfig/depthrushConfig.vcxproj @@ -139,6 +139,7 @@ + diff --git a/depthrushConfig/depthrushConfig.vcxproj.filters b/depthrushConfig/depthrushConfig.vcxproj.filters index 917980d..350adad 100644 --- a/depthrushConfig/depthrushConfig.vcxproj.filters +++ b/depthrushConfig/depthrushConfig.vcxproj.filters @@ -35,5 +35,8 @@ Header Files + + Header Files + \ No newline at end of file diff --git a/depthrushConfig/includes.h b/depthrushConfig/includes.h index a699c4d..554b0d2 100644 --- a/depthrushConfig/includes.h +++ b/depthrushConfig/includes.h @@ -4,6 +4,7 @@ #include #include #include +#include "ini.h" #include "calibration.h" #include "kinectTest.h" diff --git a/depthrushConfig/ini.h b/depthrushConfig/ini.h new file mode 100644 index 0000000..cb0b801 --- /dev/null +++ b/depthrushConfig/ini.h @@ -0,0 +1,789 @@ +/* + * The MIT License (MIT) + * Copyright (c) 2018 Danijel Durakovic + * + * 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. + * + */ + + /////////////////////////////////////////////////////////////////////////////// + // + // /mINI/ v0.9.14 + // An INI file reader and writer for the modern age. + // + /////////////////////////////////////////////////////////////////////////////// + // + // A tiny utility library for manipulating INI files with a straightforward + // API and a minimal footprint. It conforms to the (somewhat) standard INI + // format - sections and keys are case insensitive and all leading and + // trailing whitespace is ignored. Comments are lines that begin with a + // semicolon. Trailing comments are allowed on section lines. + // + // Files are read on demand, upon which data is kept in memory and the file + // is closed. This utility supports lazy writing, which only writes changes + // and updates to a file and preserves custom formatting and comments. A lazy + // write invoked by a write() call will read the output file, find what + // changes have been made and update the file accordingly. If you only need to + // generate files, use generate() instead. Section and key order is preserved + // on read, write and insert. + // + /////////////////////////////////////////////////////////////////////////////// + // + // /* BASIC USAGE EXAMPLE: */ + // + // /* read from file */ + // mINI::INIFile file("myfile.ini"); + // mINI::INIStructure ini; + // file.read(ini); + // + // /* read value; gets a reference to actual value in the structure. + // if key or section don't exist, a new empty value will be created */ + // std::string& value = ini["section"]["key"]; + // + // /* read value safely; gets a copy of value in the structure. + // does not alter the structure */ + // std::string value = ini.get("section").get("key"); + // + // /* set or update values */ + // ini["section"]["key"] = "value"; + // + // /* set multiple values */ + // ini["section2"].set({ + // {"key1", "value1"}, + // {"key2", "value2"} + // }); + // + // /* write updates back to file, preserving comments and formatting */ + // file.write(ini); + // + // /* or generate a file (overwrites the original) */ + // file.generate(ini); + // + /////////////////////////////////////////////////////////////////////////////// + // + // Long live the INI file!!! + // + /////////////////////////////////////////////////////////////////////////////// + +#ifndef MINI_INI_H_ +#define MINI_INI_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace mINI +{ + namespace INIStringUtil + { + const char* const whitespaceDelimiters = " \t\n\r\f\v"; + inline void trim(std::string& str) + { + str.erase(str.find_last_not_of(whitespaceDelimiters) + 1); + str.erase(0, str.find_first_not_of(whitespaceDelimiters)); + } +#ifndef MINI_CASE_SENSITIVE + inline void toLower(std::string& str) + { + std::transform(str.begin(), str.end(), str.begin(), [](const char c) { + return static_cast(std::tolower(c)); + }); + } +#endif + inline void replace(std::string& str, std::string const& a, std::string const& b) + { + if (!a.empty()) + { + std::size_t pos = 0; + while ((pos = str.find(a, pos)) != std::string::npos) + { + str.replace(pos, a.size(), b); + pos += b.size(); + } + } + } +#ifdef _WIN32 + const char* const endl = "\r\n"; +#else + const char* const endl = "\n"; +#endif + } + + template + class INIMap + { + private: + using T_DataIndexMap = std::unordered_map; + using T_DataItem = std::pair; + using T_DataContainer = std::vector; + using T_MultiArgs = typename std::vector>; + + T_DataIndexMap dataIndexMap; + T_DataContainer data; + + inline std::size_t setEmpty(std::string& key) + { + std::size_t index = data.size(); + dataIndexMap[key] = index; + data.emplace_back(key, T()); + return index; + } + + public: + using const_iterator = typename T_DataContainer::const_iterator; + + INIMap() { } + + INIMap(INIMap const& other) + { + std::size_t data_size = other.data.size(); + for (std::size_t i = 0; i < data_size; ++i) + { + auto const& key = other.data[i].first; + auto const& obj = other.data[i].second; + data.emplace_back(key, obj); + } + dataIndexMap = T_DataIndexMap(other.dataIndexMap); + } + + T& operator[](std::string key) + { + INIStringUtil::trim(key); +#ifndef MINI_CASE_SENSITIVE + INIStringUtil::toLower(key); +#endif + auto it = dataIndexMap.find(key); + bool hasIt = (it != dataIndexMap.end()); + std::size_t index = (hasIt) ? it->second : setEmpty(key); + return data[index].second; + } + T get(std::string key) const + { + INIStringUtil::trim(key); +#ifndef MINI_CASE_SENSITIVE + INIStringUtil::toLower(key); +#endif + auto it = dataIndexMap.find(key); + if (it == dataIndexMap.end()) + { + return T(); + } + return T(data[it->second].second); + } + bool has(std::string key) const + { + INIStringUtil::trim(key); +#ifndef MINI_CASE_SENSITIVE + INIStringUtil::toLower(key); +#endif + return (dataIndexMap.count(key) == 1); + } + void set(std::string key, T obj) + { + INIStringUtil::trim(key); +#ifndef MINI_CASE_SENSITIVE + INIStringUtil::toLower(key); +#endif + auto it = dataIndexMap.find(key); + if (it != dataIndexMap.end()) + { + data[it->second].second = obj; + } + else + { + dataIndexMap[key] = data.size(); + data.emplace_back(key, obj); + } + } + void set(T_MultiArgs const& multiArgs) + { + for (auto const& it : multiArgs) + { + auto const& key = it.first; + auto const& obj = it.second; + set(key, obj); + } + } + bool remove(std::string key) + { + INIStringUtil::trim(key); +#ifndef MINI_CASE_SENSITIVE + INIStringUtil::toLower(key); +#endif + auto it = dataIndexMap.find(key); + if (it != dataIndexMap.end()) + { + std::size_t index = it->second; + data.erase(data.begin() + index); + dataIndexMap.erase(it); + for (auto& it2 : dataIndexMap) + { + auto& vi = it2.second; + if (vi > index) + { + vi--; + } + } + return true; + } + return false; + } + void clear() + { + data.clear(); + dataIndexMap.clear(); + } + std::size_t size() const + { + return data.size(); + } + const_iterator begin() const { return data.begin(); } + const_iterator end() const { return data.end(); } + }; + + using INIStructure = INIMap>; + + namespace INIParser + { + using T_ParseValues = std::pair; + + enum class PDataType : char + { + PDATA_NONE, + PDATA_COMMENT, + PDATA_SECTION, + PDATA_KEYVALUE, + PDATA_UNKNOWN + }; + + inline PDataType parseLine(std::string line, T_ParseValues& parseData) + { + parseData.first.clear(); + parseData.second.clear(); + INIStringUtil::trim(line); + if (line.empty()) + { + return PDataType::PDATA_NONE; + } + char firstCharacter = line[0]; + if (firstCharacter == ';') + { + return PDataType::PDATA_COMMENT; + } + if (firstCharacter == '[') + { + auto commentAt = line.find_first_of(';'); + if (commentAt != std::string::npos) + { + line = line.substr(0, commentAt); + } + auto closingBracketAt = line.find_last_of(']'); + if (closingBracketAt != std::string::npos) + { + auto section = line.substr(1, closingBracketAt - 1); + INIStringUtil::trim(section); + parseData.first = section; + return PDataType::PDATA_SECTION; + } + } + auto lineNorm = line; + INIStringUtil::replace(lineNorm, "\\=", " "); + auto equalsAt = lineNorm.find_first_of('='); + if (equalsAt != std::string::npos) + { + auto key = line.substr(0, equalsAt); + INIStringUtil::trim(key); + INIStringUtil::replace(key, "\\=", "="); + auto value = line.substr(equalsAt + 1); + INIStringUtil::trim(value); + parseData.first = key; + parseData.second = value; + return PDataType::PDATA_KEYVALUE; + } + return PDataType::PDATA_UNKNOWN; + } + } + + class INIReader + { + public: + using T_LineData = std::vector; + using T_LineDataPtr = std::shared_ptr; + + bool isBOM = false; + + private: + std::ifstream fileReadStream; + T_LineDataPtr lineData; + + T_LineData readFile() + { + fileReadStream.seekg(0, std::ios::end); + const std::size_t fileSize = static_cast(fileReadStream.tellg()); + fileReadStream.seekg(0, std::ios::beg); + if (fileSize >= 3) { + const char header[3] = { + static_cast(fileReadStream.get()), + static_cast(fileReadStream.get()), + static_cast(fileReadStream.get()) + }; + isBOM = ( + header[0] == static_cast(0xEF) && + header[1] == static_cast(0xBB) && + header[2] == static_cast(0xBF) + ); + } + else { + isBOM = false; + } + std::string fileContents; + fileContents.resize(fileSize); + fileReadStream.seekg(isBOM ? 3 : 0, std::ios::beg); + fileReadStream.read(&fileContents[0], fileSize); + fileReadStream.close(); + T_LineData output; + if (fileSize == 0) + { + return output; + } + std::string buffer; + buffer.reserve(50); + for (std::size_t i = 0; i < fileSize; ++i) + { + char& c = fileContents[i]; + if (c == '\n') + { + output.emplace_back(buffer); + buffer.clear(); + continue; + } + if (c != '\0' && c != '\r') + { + buffer += c; + } + } + output.emplace_back(buffer); + return output; + } + + public: + INIReader(std::string const& filename, bool keepLineData = false) + { + fileReadStream.open(filename, std::ios::in | std::ios::binary); + if (keepLineData) + { + lineData = std::make_shared(); + } + } + ~INIReader() { } + + bool operator>>(INIStructure& data) + { + if (!fileReadStream.is_open()) + { + return false; + } + T_LineData fileLines = readFile(); + std::string section; + bool inSection = false; + INIParser::T_ParseValues parseData; + for (auto const& line : fileLines) + { + auto parseResult = INIParser::parseLine(line, parseData); + if (parseResult == INIParser::PDataType::PDATA_SECTION) + { + inSection = true; + data[section = parseData.first]; + } + else if (inSection && parseResult == INIParser::PDataType::PDATA_KEYVALUE) + { + auto const& key = parseData.first; + auto const& value = parseData.second; + data[section][key] = value; + } + if (lineData && parseResult != INIParser::PDataType::PDATA_UNKNOWN) + { + if (parseResult == INIParser::PDataType::PDATA_KEYVALUE && !inSection) + { + continue; + } + lineData->emplace_back(line); + } + } + return true; + } + T_LineDataPtr getLines() + { + return lineData; + } + }; + + class INIGenerator + { + private: + std::ofstream fileWriteStream; + + public: + bool prettyPrint = false; + + INIGenerator(std::string const& filename) + { + fileWriteStream.open(filename, std::ios::out | std::ios::binary); + } + ~INIGenerator() { } + + bool operator<<(INIStructure const& data) + { + if (!fileWriteStream.is_open()) + { + return false; + } + if (!data.size()) + { + return true; + } + auto it = data.begin(); + for (;;) + { + auto const& section = it->first; + auto const& collection = it->second; + fileWriteStream + << "[" + << section + << "]"; + if (collection.size()) + { + fileWriteStream << INIStringUtil::endl; + auto it2 = collection.begin(); + for (;;) + { + auto key = it2->first; + INIStringUtil::replace(key, "=", "\\="); + auto value = it2->second; + INIStringUtil::trim(value); + fileWriteStream + << key + << ((prettyPrint) ? " = " : "=") + << value; + if (++it2 == collection.end()) + { + break; + } + fileWriteStream << INIStringUtil::endl; + } + } + if (++it == data.end()) + { + break; + } + fileWriteStream << INIStringUtil::endl; + if (prettyPrint) + { + fileWriteStream << INIStringUtil::endl; + } + } + return true; + } + }; + + class INIWriter + { + private: + using T_LineData = std::vector; + using T_LineDataPtr = std::shared_ptr; + + std::string filename; + + T_LineData getLazyOutput(T_LineDataPtr const& lineData, INIStructure& data, INIStructure& original) + { + T_LineData output; + INIParser::T_ParseValues parseData; + std::string sectionCurrent; + bool parsingSection = false; + bool continueToNextSection = false; + bool discardNextEmpty = false; + bool writeNewKeys = false; + std::size_t lastKeyLine = 0; + for (auto line = lineData->begin(); line != lineData->end(); ++line) + { + if (!writeNewKeys) + { + auto parseResult = INIParser::parseLine(*line, parseData); + if (parseResult == INIParser::PDataType::PDATA_SECTION) + { + if (parsingSection) + { + writeNewKeys = true; + parsingSection = false; + --line; + continue; + } + sectionCurrent = parseData.first; + if (data.has(sectionCurrent)) + { + parsingSection = true; + continueToNextSection = false; + discardNextEmpty = false; + output.emplace_back(*line); + lastKeyLine = output.size(); + } + else + { + continueToNextSection = true; + discardNextEmpty = true; + continue; + } + } + else if (parseResult == INIParser::PDataType::PDATA_KEYVALUE) + { + if (continueToNextSection) + { + continue; + } + if (data.has(sectionCurrent)) + { + auto& collection = data[sectionCurrent]; + auto const& key = parseData.first; + auto const& value = parseData.second; + if (collection.has(key)) + { + auto outputValue = collection[key]; + if (value == outputValue) + { + output.emplace_back(*line); + } + else + { + INIStringUtil::trim(outputValue); + auto lineNorm = *line; + INIStringUtil::replace(lineNorm, "\\=", " "); + auto equalsAt = lineNorm.find_first_of('='); + auto valueAt = lineNorm.find_first_not_of( + INIStringUtil::whitespaceDelimiters, + equalsAt + 1 + ); + std::string outputLine = line->substr(0, valueAt); + if (prettyPrint && equalsAt + 1 == valueAt) + { + outputLine += " "; + } + outputLine += outputValue; + output.emplace_back(outputLine); + } + lastKeyLine = output.size(); + } + } + } + else + { + if (discardNextEmpty && line->empty()) + { + discardNextEmpty = false; + } + else if (parseResult != INIParser::PDataType::PDATA_UNKNOWN) + { + output.emplace_back(*line); + } + } + } + if (writeNewKeys || std::next(line) == lineData->end()) + { + T_LineData linesToAdd; + if (data.has(sectionCurrent) && original.has(sectionCurrent)) + { + auto const& collection = data[sectionCurrent]; + auto const& collectionOriginal = original[sectionCurrent]; + for (auto const& it : collection) + { + auto key = it.first; + if (collectionOriginal.has(key)) + { + continue; + } + auto value = it.second; + INIStringUtil::replace(key, "=", "\\="); + INIStringUtil::trim(value); + linesToAdd.emplace_back( + key + ((prettyPrint) ? " = " : "=") + value + ); + } + } + if (!linesToAdd.empty()) + { + output.insert( + output.begin() + lastKeyLine, + linesToAdd.begin(), + linesToAdd.end() + ); + } + if (writeNewKeys) + { + writeNewKeys = false; + --line; + } + } + } + for (auto const& it : data) + { + auto const& section = it.first; + if (original.has(section)) + { + continue; + } + if (prettyPrint && output.size() > 0 && !output.back().empty()) + { + output.emplace_back(); + } + output.emplace_back("[" + section + "]"); + auto const& collection = it.second; + for (auto const& it2 : collection) + { + auto key = it2.first; + auto value = it2.second; + INIStringUtil::replace(key, "=", "\\="); + INIStringUtil::trim(value); + output.emplace_back( + key + ((prettyPrint) ? " = " : "=") + value + ); + } + } + return output; + } + + public: + bool prettyPrint = false; + + INIWriter(std::string const& filename) + : filename(filename) + { + } + ~INIWriter() { } + + bool operator<<(INIStructure& data) + { + struct stat buf; + bool fileExists = (stat(filename.c_str(), &buf) == 0); + if (!fileExists) + { + INIGenerator generator(filename); + generator.prettyPrint = prettyPrint; + return generator << data; + } + INIStructure originalData; + T_LineDataPtr lineData; + bool readSuccess = false; + bool fileIsBOM = false; + { + INIReader reader(filename, true); + if ((readSuccess = reader >> originalData)) + { + lineData = reader.getLines(); + fileIsBOM = reader.isBOM; + } + } + if (!readSuccess) + { + return false; + } + T_LineData output = getLazyOutput(lineData, data, originalData); + std::ofstream fileWriteStream(filename, std::ios::out | std::ios::binary); + if (fileWriteStream.is_open()) + { + if (fileIsBOM) { + const char utf8_BOM[3] = { + static_cast(0xEF), + static_cast(0xBB), + static_cast(0xBF) + }; + fileWriteStream.write(utf8_BOM, 3); + } + if (output.size()) + { + auto line = output.begin(); + for (;;) + { + fileWriteStream << *line; + if (++line == output.end()) + { + break; + } + fileWriteStream << INIStringUtil::endl; + } + } + return true; + } + return false; + } + }; + + class INIFile + { + private: + std::string filename; + + public: + INIFile(std::string const& filename) + : filename(filename) + { } + + ~INIFile() { } + + bool read(INIStructure& data) const + { + if (data.size()) + { + data.clear(); + } + if (filename.empty()) + { + return false; + } + INIReader reader(filename); + return reader >> data; + } + bool generate(INIStructure const& data, bool pretty = false) const + { + if (filename.empty()) + { + return false; + } + INIGenerator generator(filename); + generator.prettyPrint = pretty; + return generator << data; + } + bool write(INIStructure& data, bool pretty = false) const + { + if (filename.empty()) + { + return false; + } + INIWriter writer(filename); + writer.prettyPrint = pretty; + return writer << data; + } + }; +} + +#endif // MINI_INI_H_ \ No newline at end of file From 256161d1a96339584fa36f6cabfaad16399062d1 Mon Sep 17 00:00:00 2001 From: doomertheboomer Date: Fri, 28 Jul 2023 19:37:33 +0700 Subject: [PATCH 6/7] add calibration to the game --- depthrush/drs.cpp | 36 ++++++++++++++++++++++++++++++------ 1 file changed, 30 insertions(+), 6 deletions(-) diff --git a/depthrush/drs.cpp b/depthrush/drs.cpp index 4709ac1..ed17e85 100644 --- a/depthrush/drs.cpp +++ b/depthrush/drs.cpp @@ -228,9 +228,27 @@ void pollKinect() { return 1; } + // load config from ini + std::cout << "Loading depthrush.ini\n"; + mINI::INIFile file("depthrush.ini"); + mINI::INIStructure ini; + file.read(ini); + std::string& readValue = ini["calibration"]["xGrad"]; + float xGrad = std::stof(readValue); + readValue = ini["calibration"]["xOffset"]; + float xOffset = std::stof(readValue); + readValue = ini["calibration"]["yGrad"]; + float yGrad = std::stof(readValue); + readValue = ini["calibration"]["yOffset"]; + float yOffset = std::stof(readValue); + readValue = ini["calibration"]["zGrad"]; + float zGrad = std::stof(readValue); + readValue = ini["calibration"]["zOffset"]; + float zOffset = std::stof(readValue); + // main loop to read and process skeleton data NUI_SKELETON_FRAME skeletonFrame = { 0 }; - float errorMargin = 0.02; + float errorMargin = 0.03; while (true) { // get the latest skeleton frame hr = NuiSkeletonGetNextFrame(0, &skeletonFrame); @@ -249,24 +267,30 @@ void pollKinect() { //std::cout << "Left Leg: X = " << leftLegPos.x << ", Y = " << leftLegPos.y << ", Z = " << leftLegPos.z << std::endl; //std::cout << "Right Leg: X = " << rightLegPos.x << ", Y = " << rightLegPos.y << ", Z = " << rightLegPos.z << std::endl; - feet[1].event.x = leftLegPos.x; + feet[1].event.x = xGrad * leftLegPos.x + xOffset; feet[1].event.y = 0.5; - feet[2].event.x = rightLegPos.x; + feet[2].event.x = xGrad * rightLegPos.x + xOffset; feet[2].event.y = 0.5; + // Fix feet height + float fixedLeft = leftLegPos.y - (yGrad * leftLegPos.z + yOffset); + float fixedRight = rightLegPos.y - (yGrad * rightLegPos.z + yOffset); + // check for stepping - if (leftLegPos.y > (rightLegPos.y + errorMargin)) { + if (fixedLeft > (fixedRight + errorMargin)) { feet[2].touching = true; feet[1].touching = false; - + // std::cout << "right step\n"; } - else if (rightLegPos.y > (leftLegPos.y + errorMargin)) { + else if (fixedRight > (fixedLeft + errorMargin)) { feet[1].touching = true; feet[2].touching = false; + // std::cout << "left step \n"; } else { feet[1].touching = true; feet[2].touching = true; + // std::cout << "both step\n"; } } From 48e641ec3c6ff78f1ea7e0eee56097cc81c30c6a Mon Sep 17 00:00:00 2001 From: doomertheboomer Date: Fri, 28 Jul 2023 19:42:14 +0700 Subject: [PATCH 7/7] update readme --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 5e4b2ab..5274c9c 100644 --- a/README.md +++ b/README.md @@ -3,4 +3,5 @@ Work in progress Kinect driver for Dancerush Stardom. ## Installation 1. Patch SpiceTools to disable all DRS pad hooks. -2. Copy `depthrush.dll` to the same folder as spice64 and use spicecfg to inject depthrush. +2. Copy all three of the files from to the same folder as spice64 and use spicecfg to inject `depthrush.dll`. +3. **IMPORTANT:** Open `depthrushConfig.exe` and calibrate your Kinect environment! Otherwise it won't work well with the game.