diff --git a/Navmesher/Navmesher.vcxproj b/Navmesher/Navmesher.vcxproj
index 3f97a8e..1cc51af 100644
--- a/Navmesher/Navmesher.vcxproj
+++ b/Navmesher/Navmesher.vcxproj
@@ -104,7 +104,7 @@
true
_DEBUG;_WINDOWS;%(PreprocessorDefinitions)
true
- ..\imgui\backends;..\glfw\include;..\imgui;%(AdditionalIncludeDirectories)
+ ..\SizedBuffer;..\ESPReader;..\imgui\backends;..\glfw\include;..\imgui;%(AdditionalIncludeDirectories)
Console
@@ -159,6 +159,14 @@
+
+
+ {c403cba2-86ae-4442-87cf-26b283a549c2}
+
+
+ {cb1c8f66-5b90-4de7-890b-f6430daaf25f}
+
+
diff --git a/Navmesher/main.cpp b/Navmesher/main.cpp
index c1d7834..5fc73a0 100644
--- a/Navmesher/main.cpp
+++ b/Navmesher/main.cpp
@@ -1,42 +1,152 @@
-// Dear ImGui: standalone example application for GLFW + OpenGL 3, using programmable pipeline
-// (GLFW is a cross-platform general purpose library for handling windows, inputs, OpenGL/Vulkan/Metal graphics context creation, etc.)
-// If you are new to Dear ImGui, read documentation from the docs/ folder + read the top of imgui.cpp.
-// Read online: https://github.com/ocornut/imgui/tree/master/docs
+// Based on Dear ImGui GLFW + OpenGL 3 standalone example
+#include "ESPReader.h"
#include "imgui.h"
#include "imgui_impl_glfw.h"
#include "imgui_impl_opengl3.h"
#include
+#include
+#undef NDEBUG
+#include
+#define NDEBUG
#include // Will drag system OpenGL headers
const float BASE_FONT_SIZE = 13;
+const ImGuiWindowFlags window_flags = ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoSavedSettings;
+
+MetaTree ReadESP(const char *filename);
+
+GLFWwindow *Setup(void);
+void NewFrame(void);
+void Fullscreen(void);
+void Render(GLFWwindow *window, const ImVec4 clear_color);
+void Cleanup(GLFWwindow *window);
+void ChangeScale(bool *scale_update, const float scale);
+
+void ScalingWidget(bool *scale_update, float *scale);
+void ESPTreeWalk(MetaNode *m);
static void glfw_error_callback(int error, const char *description)
{
fprintf(stderr, "Glfw Error %d: %s\n", error, description);
}
-void ChangeScale(bool *scale_update, const float scale);
-void ScalingWidget(bool *scale_update, float *scale);
-
int main(int, char **)
{
- // Setup window
- glfwSetErrorCallback(glfw_error_callback);
- if (!glfwInit())
+ GLFWwindow *window = Setup();
+ if (!window)
return 1;
- // GL 3.0 + GLSL 130
+ MetaTree m = ReadESP("Skyrim.esm");
+ m.root->user = (void *)true;
+
+ // Our state
+ ImVec4 clear_color = ImVec4(0.45f, 0.55f, 0.60f, 1.00f);
+ bool scale_update = false;
+ float scale = 1.0f;
+
+ // Main loop
+ while (!glfwWindowShouldClose(window))
+ {
+ // Poll and handle events (inputs, window resize, etc.)
+ // You can read the io.WantCaptureMouse, io.WantCaptureKeyboard flags to tell if dear imgui wants to use your inputs.
+ // - When io.WantCaptureMouse is true, do not dispatch mouse input data to your main application, or clear/overwrite your copy of the mouse data.
+ // - When io.WantCaptureKeyboard is true, do not dispatch keyboard input data to your main application, or clear/overwrite your copy of the keyboard data.
+ // Generally you may always pass all inputs to dear imgui, and hide them from your application based on those two flags.
+ glfwPollEvents();
+
+ // Interface scaling
+ ChangeScale(&scale_update, scale);
+
+ // Start the Dear ImGui frame
+ NewFrame();
+
+ // Setup for fullscreen
+ Fullscreen();
+
+ // Begin window
+ ImGui::Begin("ESP Browser", NULL, window_flags);
+
+ ScalingWidget(&scale_update, &scale);
+
+ ESPTreeWalk(m.root);
+
+ // End window
+ ImGui::End();
+
+ Render(window, clear_color);
+ }
+
+ free(m.esp.data);
+ free(m.tree.data);
+
+ Cleanup(window);
+
+ return 0;
+}
+
+MetaTree ReadESP(const char *filename)
+{
+ errno_t ret = 0;
+
+ SizedBuf esp_buf = { 0 };
+ ESPStats stats = { 0 };
+ SizedBuf tree_buf = { 0 };
+ MetaTree tree = { 0 };
+
+ // Read in the esp file
+ {
+ FILE *fp;
+ ret = fopen_s(&fp, "Skyrim.esm", "rb");
+ assert(fp && !ret);
+
+ fseek(fp, 0L, SEEK_END);
+ esp_buf.size = ftell(fp);
+ rewind(fp);
+
+ esp_buf.data = (char *)malloc(esp_buf.size);
+ assert(esp_buf.data);
+
+ size_t read = fread(esp_buf.data, sizeof(char), esp_buf.size, fp);
+ fclose(fp);
+ assert(read == esp_buf.size);
+ }
+
+ // Calculate esp stats
+ stats = espr_stats(esp_buf);
+
+ // Construct the meta tree
+ {
+ tree_buf.size = espr_tree_size(stats);
+ tree_buf.data = (char *)malloc(tree_buf.size);
+ assert(tree_buf.data);
+ tree = espr_create_tree(esp_buf, tree_buf);
+ }
+
+ // lazy file pointer cleanup
+ _fcloseall();
+
+ return tree;
+}
+
+GLFWwindow *Setup(void)
+{
+ // Setup window
+ glfwSetErrorCallback(glfw_error_callback);
+ if (!glfwInit())
+ return NULL;
+
+ // GL 3.0 + GLSL 130
const char *glsl_version = "#version 130";
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
- // Create window with graphics context
+ // Create window with graphics context
GLFWwindow *window = glfwCreateWindow(1920, 1080, "Dear ImGui GLFW+OpenGL3 example", NULL, NULL);
if (window == NULL)
- return 1;
+ return NULL;
glfwMakeContextCurrent(window);
glfwSwapInterval(1); // Enable vsync
@@ -67,80 +177,60 @@ int main(int, char **)
ImGui_ImplGlfw_InitForOpenGL(window, true);
ImGui_ImplOpenGL3_Init(glsl_version);
- // Our state
- ImVec4 clear_color = ImVec4(0.45f, 0.55f, 0.60f, 1.00f);
- bool scale_update = false;
- float scale = 1.0f;
+ return window;
+}
- // Main loop
- while (!glfwWindowShouldClose(window))
+void NewFrame(void)
+{
+ ImGui_ImplOpenGL3_NewFrame();
+ ImGui_ImplGlfw_NewFrame();
+ ImGui::NewFrame();
+
+}
+
+void Fullscreen(void)
+{
+ const ImGuiViewport *viewport = ImGui::GetMainViewport();
+ ImGui::SetNextWindowPos(viewport->WorkPos);
+ ImGui::SetNextWindowSize(viewport->WorkSize);
+}
+
+
+void Render(GLFWwindow * window, const ImVec4 clear_color)
+{
+ ImGuiIO &io = ImGui::GetIO();
+
+ ImGui::Render();
+
+ int display_w, display_h;
+ glfwGetFramebufferSize(window, &display_w, &display_h);
+ glViewport(0, 0, display_w, display_h);
+ glClearColor(clear_color.x * clear_color.w, clear_color.y * clear_color.w, clear_color.z * clear_color.w, clear_color.w);
+ glClear(GL_COLOR_BUFFER_BIT);
+ ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
+
+ // Update and Render additional Platform Windows
+ // (Platform functions may change the current OpenGL context, so we save/restore it to make it easier to paste this code elsewhere.
+ // For this specific demo app we could also call glfwMakeContextCurrent(window) directly)
+ if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable)
{
- // Poll and handle events (inputs, window resize, etc.)
- // You can read the io.WantCaptureMouse, io.WantCaptureKeyboard flags to tell if dear imgui wants to use your inputs.
- // - When io.WantCaptureMouse is true, do not dispatch mouse input data to your main application, or clear/overwrite your copy of the mouse data.
- // - When io.WantCaptureKeyboard is true, do not dispatch keyboard input data to your main application, or clear/overwrite your copy of the keyboard data.
- // Generally you may always pass all inputs to dear imgui, and hide them from your application based on those two flags.
- glfwPollEvents();
-
- static ImGuiWindowFlags flags = ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoSavedSettings;
-
- // Interface scaling
- ChangeScale(&scale_update, scale);
-
- // Start the Dear ImGui frame
- ImGui_ImplOpenGL3_NewFrame();
- ImGui_ImplGlfw_NewFrame();
- ImGui::NewFrame();
-
- // setup for fullscreen
- const ImGuiViewport *viewport = ImGui::GetMainViewport();
- ImGui::SetNextWindowPos(viewport->WorkPos);
- ImGui::SetNextWindowSize(viewport->WorkSize);
-
- ImGui::Begin("ESP Browser", NULL, flags);
-
- ScalingWidget(&scale_update, &scale);
-
- if (ImGui::TreeNode("Test"))
- {
- ImGui::Text("Text");
- ImGui::TreePop();
- }
-
- ImGui::End();
-
- // Rendering
- ImGui::Render();
- int display_w, display_h;
- glfwGetFramebufferSize(window, &display_w, &display_h);
- glViewport(0, 0, display_w, display_h);
- glClearColor(clear_color.x * clear_color.w, clear_color.y * clear_color.w, clear_color.z * clear_color.w, clear_color.w);
- glClear(GL_COLOR_BUFFER_BIT);
- ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
-
- // Update and Render additional Platform Windows
- // (Platform functions may change the current OpenGL context, so we save/restore it to make it easier to paste this code elsewhere.
- // For this specific demo app we could also call glfwMakeContextCurrent(window) directly)
- if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable)
- {
- GLFWwindow *backup_current_context = glfwGetCurrentContext();
- ImGui::UpdatePlatformWindows();
- ImGui::RenderPlatformWindowsDefault();
- glfwMakeContextCurrent(backup_current_context);
- }
-
- glfwSwapBuffers(window);
+ GLFWwindow *backup_current_context = glfwGetCurrentContext();
+ ImGui::UpdatePlatformWindows();
+ ImGui::RenderPlatformWindowsDefault();
+ glfwMakeContextCurrent(backup_current_context);
}
- // Cleanup
+ glfwSwapBuffers(window);
+}
+
+void Cleanup(GLFWwindow *window)
+{
ImGui_ImplOpenGL3_Shutdown();
ImGui_ImplGlfw_Shutdown();
ImGui::DestroyContext();
glfwDestroyWindow(window);
glfwTerminate();
-
- return 0;
}
void ChangeScale(bool *scale_update, const float scale)
@@ -152,7 +242,8 @@ void ChangeScale(bool *scale_update, const float scale)
if (*scale_update) {
ImFontConfig font_config;
- font_config.SizePixels = (BASE_FONT_SIZE * scale);
+ // Truncate to 2 decimal places
+ font_config.SizePixels = ((float)(int)(BASE_FONT_SIZE * scale * 100)) / 100;
io.Fonts->Clear();
io.Fonts->AddFontDefault(&font_config);
ImGui_ImplOpenGL3_UpdateFontsTexture();
@@ -179,3 +270,22 @@ void ScalingWidget(bool *scale_update, float *scale)
ImGui::PopItemWidth();
}
+void ESPTreeWalk(MetaNode *m)
+{
+ char buf[1024] = { 0 };
+ SizedBuf sb = { buf, 1024 };
+ espr_meta_string(m, sb);
+ if (m->n.type == NT_RECORD) {
+ ImGui::BulletText(buf);
+ } else {
+ if (ImGui::TreeNode(buf)) {
+ MetaNode *c = m->first_child;
+ while (c) {
+ ESPTreeWalk(c);
+ c = c->next;
+ }
+ ImGui::TreePop();
+ }
+ }
+}
+
diff --git a/espReader/ESPReader.h b/espReader/ESPReader.h
index a6d166a..1880e9b 100644
--- a/espReader/ESPReader.h
+++ b/espReader/ESPReader.h
@@ -247,7 +247,7 @@ extern "C" {
*/
struct meta_node {
Node n;
- MetaNode *parent;
+ void *user;
MetaNode *first_child;
MetaNode *last_child;
MetaNode *prev;
@@ -255,6 +255,9 @@ extern "C" {
};
struct meta_tree {
+ // hack, not usable when modifying tree
+ SizedBuf tree;
+ SizedBuf esp;
MetaNode *root;
size_t size;
};
@@ -395,6 +398,8 @@ extern "C" {
void espr_serialize(MetaTree tree, SizedBuf out);
+ void espr_meta_string(MetaNode *m, SizedBuf str_buf);
+
// End C++ guard
#ifdef __cplusplus
}
diff --git a/espReader/Reader.c b/espReader/Reader.c
index d401909..a40102f 100644
--- a/espReader/Reader.c
+++ b/espReader/Reader.c
@@ -11,6 +11,7 @@
#include
#include
#include
+#include
#include
@@ -468,8 +469,10 @@ void litcopy(struct str_buf *sb, struct str_lit lit)
void num_str(struct str_buf *sb, unsigned long num, int radix)
{
+ _CrtSetDebugFillThreshold(0);
errno_t ret = _ultoa_s(num, sb->buf, sb->size, radix);
assert(ret == 0);
+ _CrtSetDebugFillThreshold(SIZE_MAX);
int len = (int)strlen(sb->buf);
sb->size -= len;
sb->buf += len;
@@ -629,6 +632,8 @@ Timestamp convert_ts(uint16_t ts)
MetaTree espr_create_tree(SizedBuf in, SizedBuf tree)
{
+ MetaTree mtree = { .esp = in, .tree = tree };
+
// create root node
MetaNode *root = NULL;
{
@@ -641,7 +646,10 @@ MetaTree espr_create_tree(SizedBuf in, SizedBuf tree)
struct walker_callbacks cb = { .pre = create_tree_cb, .data = &tree };
espr_walk(in, cb, root);
- return (MetaTree) { .root = root, .size = in.size };
+ mtree.root = root;
+ mtree.size = in.size;
+
+ return mtree;
}
void create_tree_cb(
@@ -667,7 +675,6 @@ void create_tree_cb(
// construct new node
*m = (MetaNode){ 0 }; // zero/null unused
m->n = n;
- m->parent = p;
m->prev = p->last_child;
// the linked list of children may not already exist
if (p->last_child)
@@ -730,3 +737,33 @@ void serialize_cb(MetaNode *m, void *data) {
}
}
+void espr_meta_string(MetaNode *m, SizedBuf str_buf)
+{
+ // root node
+ if (!m->n.data) {
+ snprintf(str_buf.data, str_buf.size, "Root");
+ return;
+ }
+
+ char buf[1024] = { 0 };
+ struct str_buf sb = { .buf = buf, .size = 1024 };
+
+ switch (m->n.type) {
+ case NT_GROUP:
+ {
+ group_label_str(&sb, m->n.header.group);
+ const char *lbl = group_type_strings[m->n.header.group->type].lit;
+ snprintf(str_buf.data, str_buf.size, "GRUP; %s; %s", lbl, buf);
+ break;
+ }
+ case NT_RECORD:
+ {
+ type_str(&sb, m->n.header.record->type);
+ snprintf(str_buf.data, str_buf.size, "%s; FormID[%08x]", buf, m->n.header.record->formid);
+ break;
+ }
+ default:
+ assert(false);
+ }
+}
+