ESP Tree view.
This commit is contained in:
@@ -104,7 +104,7 @@
|
|||||||
<SDLCheck>true</SDLCheck>
|
<SDLCheck>true</SDLCheck>
|
||||||
<PreprocessorDefinitions>_DEBUG;_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
<PreprocessorDefinitions>_DEBUG;_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||||
<ConformanceMode>true</ConformanceMode>
|
<ConformanceMode>true</ConformanceMode>
|
||||||
<AdditionalIncludeDirectories>..\imgui\backends;..\glfw\include;..\imgui;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
<AdditionalIncludeDirectories>..\SizedBuffer;..\ESPReader;..\imgui\backends;..\glfw\include;..\imgui;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
<Link>
|
<Link>
|
||||||
<SubSystem>Console</SubSystem>
|
<SubSystem>Console</SubSystem>
|
||||||
@@ -159,6 +159,14 @@
|
|||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<None Include="..\imgui\misc\debuggers\imgui.natvis" />
|
<None Include="..\imgui\misc\debuggers\imgui.natvis" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\espReader\espReader.vcxproj">
|
||||||
|
<Project>{c403cba2-86ae-4442-87cf-26b283a549c2}</Project>
|
||||||
|
</ProjectReference>
|
||||||
|
<ProjectReference Include="..\SizedBuffer\SizedBuffer.vcxproj">
|
||||||
|
<Project>{cb1c8f66-5b90-4de7-890b-f6430daaf25f}</Project>
|
||||||
|
</ProjectReference>
|
||||||
|
</ItemGroup>
|
||||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||||
<ImportGroup Label="ExtensionTargets">
|
<ImportGroup Label="ExtensionTargets">
|
||||||
</ImportGroup>
|
</ImportGroup>
|
||||||
|
|||||||
@@ -1,42 +1,152 @@
|
|||||||
// Dear ImGui: standalone example application for GLFW + OpenGL 3, using programmable pipeline
|
// Based on Dear ImGui GLFW + OpenGL 3 standalone example
|
||||||
// (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
|
|
||||||
|
|
||||||
|
#include "ESPReader.h"
|
||||||
#include "imgui.h"
|
#include "imgui.h"
|
||||||
#include "imgui_impl_glfw.h"
|
#include "imgui_impl_glfw.h"
|
||||||
#include "imgui_impl_opengl3.h"
|
#include "imgui_impl_opengl3.h"
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
#include <cstdlib>
|
||||||
|
#undef NDEBUG
|
||||||
|
#include <assert.h>
|
||||||
|
#define NDEBUG
|
||||||
#include <GLFW/glfw3.h> // Will drag system OpenGL headers
|
#include <GLFW/glfw3.h> // Will drag system OpenGL headers
|
||||||
|
|
||||||
const float BASE_FONT_SIZE = 13;
|
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)
|
static void glfw_error_callback(int error, const char *description)
|
||||||
{
|
{
|
||||||
fprintf(stderr, "Glfw Error %d: %s\n", error, 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 **)
|
int main(int, char **)
|
||||||
{
|
{
|
||||||
// Setup window
|
GLFWwindow *window = Setup();
|
||||||
glfwSetErrorCallback(glfw_error_callback);
|
if (!window)
|
||||||
if (!glfwInit())
|
|
||||||
return 1;
|
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";
|
const char *glsl_version = "#version 130";
|
||||||
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
|
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
|
||||||
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2);
|
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2);
|
||||||
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
|
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
|
||||||
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
|
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);
|
GLFWwindow *window = glfwCreateWindow(1920, 1080, "Dear ImGui GLFW+OpenGL3 example", NULL, NULL);
|
||||||
if (window == NULL)
|
if (window == NULL)
|
||||||
return 1;
|
return NULL;
|
||||||
glfwMakeContextCurrent(window);
|
glfwMakeContextCurrent(window);
|
||||||
glfwSwapInterval(1); // Enable vsync
|
glfwSwapInterval(1); // Enable vsync
|
||||||
|
|
||||||
@@ -67,80 +177,60 @@ int main(int, char **)
|
|||||||
ImGui_ImplGlfw_InitForOpenGL(window, true);
|
ImGui_ImplGlfw_InitForOpenGL(window, true);
|
||||||
ImGui_ImplOpenGL3_Init(glsl_version);
|
ImGui_ImplOpenGL3_Init(glsl_version);
|
||||||
|
|
||||||
// Our state
|
return window;
|
||||||
ImVec4 clear_color = ImVec4(0.45f, 0.55f, 0.60f, 1.00f);
|
}
|
||||||
bool scale_update = false;
|
|
||||||
float scale = 1.0f;
|
|
||||||
|
|
||||||
// Main loop
|
void NewFrame(void)
|
||||||
while (!glfwWindowShouldClose(window))
|
{
|
||||||
|
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.)
|
GLFWwindow *backup_current_context = glfwGetCurrentContext();
|
||||||
// You can read the io.WantCaptureMouse, io.WantCaptureKeyboard flags to tell if dear imgui wants to use your inputs.
|
ImGui::UpdatePlatformWindows();
|
||||||
// - When io.WantCaptureMouse is true, do not dispatch mouse input data to your main application, or clear/overwrite your copy of the mouse data.
|
ImGui::RenderPlatformWindowsDefault();
|
||||||
// - When io.WantCaptureKeyboard is true, do not dispatch keyboard input data to your main application, or clear/overwrite your copy of the keyboard data.
|
glfwMakeContextCurrent(backup_current_context);
|
||||||
// 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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Cleanup
|
glfwSwapBuffers(window);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Cleanup(GLFWwindow *window)
|
||||||
|
{
|
||||||
ImGui_ImplOpenGL3_Shutdown();
|
ImGui_ImplOpenGL3_Shutdown();
|
||||||
ImGui_ImplGlfw_Shutdown();
|
ImGui_ImplGlfw_Shutdown();
|
||||||
ImGui::DestroyContext();
|
ImGui::DestroyContext();
|
||||||
|
|
||||||
glfwDestroyWindow(window);
|
glfwDestroyWindow(window);
|
||||||
glfwTerminate();
|
glfwTerminate();
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ChangeScale(bool *scale_update, const float scale)
|
void ChangeScale(bool *scale_update, const float scale)
|
||||||
@@ -152,7 +242,8 @@ void ChangeScale(bool *scale_update, const float scale)
|
|||||||
|
|
||||||
if (*scale_update) {
|
if (*scale_update) {
|
||||||
ImFontConfig font_config;
|
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->Clear();
|
||||||
io.Fonts->AddFontDefault(&font_config);
|
io.Fonts->AddFontDefault(&font_config);
|
||||||
ImGui_ImplOpenGL3_UpdateFontsTexture();
|
ImGui_ImplOpenGL3_UpdateFontsTexture();
|
||||||
@@ -179,3 +270,22 @@ void ScalingWidget(bool *scale_update, float *scale)
|
|||||||
ImGui::PopItemWidth();
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -247,7 +247,7 @@ extern "C" {
|
|||||||
*/
|
*/
|
||||||
struct meta_node {
|
struct meta_node {
|
||||||
Node n;
|
Node n;
|
||||||
MetaNode *parent;
|
void *user;
|
||||||
MetaNode *first_child;
|
MetaNode *first_child;
|
||||||
MetaNode *last_child;
|
MetaNode *last_child;
|
||||||
MetaNode *prev;
|
MetaNode *prev;
|
||||||
@@ -255,6 +255,9 @@ extern "C" {
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct meta_tree {
|
struct meta_tree {
|
||||||
|
// hack, not usable when modifying tree
|
||||||
|
SizedBuf tree;
|
||||||
|
SizedBuf esp;
|
||||||
MetaNode *root;
|
MetaNode *root;
|
||||||
size_t size;
|
size_t size;
|
||||||
};
|
};
|
||||||
@@ -395,6 +398,8 @@ extern "C" {
|
|||||||
|
|
||||||
void espr_serialize(MetaTree tree, SizedBuf out);
|
void espr_serialize(MetaTree tree, SizedBuf out);
|
||||||
|
|
||||||
|
void espr_meta_string(MetaNode *m, SizedBuf str_buf);
|
||||||
|
|
||||||
// End C++ guard
|
// End C++ guard
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,6 +11,7 @@
|
|||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <intrin.h>
|
#include <intrin.h>
|
||||||
#include <ammintrin.h>
|
#include <ammintrin.h>
|
||||||
|
#include <crtdbg.h>
|
||||||
|
|
||||||
#include <io.h>
|
#include <io.h>
|
||||||
|
|
||||||
@@ -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)
|
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);
|
errno_t ret = _ultoa_s(num, sb->buf, sb->size, radix);
|
||||||
assert(ret == 0);
|
assert(ret == 0);
|
||||||
|
_CrtSetDebugFillThreshold(SIZE_MAX);
|
||||||
int len = (int)strlen(sb->buf);
|
int len = (int)strlen(sb->buf);
|
||||||
sb->size -= len;
|
sb->size -= len;
|
||||||
sb->buf += len;
|
sb->buf += len;
|
||||||
@@ -629,6 +632,8 @@ Timestamp convert_ts(uint16_t ts)
|
|||||||
|
|
||||||
MetaTree espr_create_tree(SizedBuf in, SizedBuf tree)
|
MetaTree espr_create_tree(SizedBuf in, SizedBuf tree)
|
||||||
{
|
{
|
||||||
|
MetaTree mtree = { .esp = in, .tree = tree };
|
||||||
|
|
||||||
// create root node
|
// create root node
|
||||||
MetaNode *root = NULL;
|
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 };
|
struct walker_callbacks cb = { .pre = create_tree_cb, .data = &tree };
|
||||||
espr_walk(in, cb, root);
|
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(
|
void create_tree_cb(
|
||||||
@@ -667,7 +675,6 @@ void create_tree_cb(
|
|||||||
// construct new node
|
// construct new node
|
||||||
*m = (MetaNode){ 0 }; // zero/null unused
|
*m = (MetaNode){ 0 }; // zero/null unused
|
||||||
m->n = n;
|
m->n = n;
|
||||||
m->parent = p;
|
|
||||||
m->prev = p->last_child;
|
m->prev = p->last_child;
|
||||||
// the linked list of children may not already exist
|
// the linked list of children may not already exist
|
||||||
if (p->last_child)
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user