Skip to content

Commit

Permalink
Theoretically, integrate with trainwreck. (#111)
Browse files Browse the repository at this point in the history
  • Loading branch information
ceejbot authored Jan 17, 2024
1 parent 4678c1e commit c9c4ef5
Show file tree
Hide file tree
Showing 11 changed files with 273 additions and 2 deletions.
3 changes: 3 additions & 0 deletions cmake/sourcelist.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ set(headers ${headers}
src/util/helpers.h
src/util/key_path.h
src/util/offset.h
src/util/trains.h
src/util/trainwreck.h
)
set(sources ${sources}
${headers}
Expand All @@ -39,4 +41,5 @@ set(sources ${sources}
src/plugin/sinks.cpp
src/renderer/ui_renderer.cpp
src/util/helpers.cpp
src/util/trains.cpp
)
2 changes: 1 addition & 1 deletion src/controller/cycles.rs
Original file line number Diff line number Diff line change
Expand Up @@ -399,7 +399,7 @@ impl CycleData {
let config = bincode::config::standard();
let bytes: Vec<u8> = bincode::encode_to_vec(value, config).unwrap_or_default();
log::info!(
"Writing cosave data. Format version {}; save data size={} bytes;",
"Writing SKSE cosave data. Format version {}; save data size={} bytes.",
CycleData::serialize_version(),
bytes.len()
);
Expand Down
5 changes: 5 additions & 0 deletions src/controller/facade.rs
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,11 @@ pub fn clear_cache() {
control::get().cache.clear();
}

/// Crash logger support.
pub fn cache_size() -> usize {
control::get().cache.len()
}

/// This is straight-up papyrus support. We choose to return -1 to signal
/// failure because our use case is as array indexes in papyrus.
pub fn string_to_int(number: String) -> i32 {
Expand Down
4 changes: 4 additions & 0 deletions src/data/item_cache.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,10 @@ impl ItemCache {
}
}

pub fn len(&self) -> usize {
self.lru.len()
}

/// On load from save, we do not bother attempting to reconcile what
/// we have cached with what the save state is. We merely enjoy the
/// eternal sunshine of the spotless mind.
Expand Down
5 changes: 4 additions & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -291,6 +291,10 @@ pub mod plugin {
/// Decode a null-terminated C string from whatever it is to utf-8.
fn cstr_to_utf8(bytes_ffi: &CxxVector<u8>) -> String;

/// If we're registered with the trainwreck crash logger, and we're in
/// the process of crashing, try to provide info for the Trainwreck section.
fn cache_size() -> usize;

/// Trigger rust to read config, figure out what the player has equipped,
/// and figure out what it should draw.
fn initialize_hud();
Expand Down Expand Up @@ -555,7 +559,6 @@ pub mod plugin {
fn startAlphaTransition(fade_in: bool, alpha: f32);
/// Set the max alpha value the HUD is allowed to reach. From user settings.
fn setMaxAlpha(max: f32);

}

// A verbose shim between Rust and the PlayerCharacter type.
Expand Down
6 changes: 6 additions & 0 deletions src/main.cpp
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
#include "SKSE/Interfaces.h"
#include "cosave.h"
#include "inventory.h"
#include "log.h"
#include "menus.h"
#include "papyrus.h"
#include "sinks.h"
#include "trains.h"
#include "ui_renderer.h"

#include "lib.rs.h"
Expand Down Expand Up @@ -57,6 +59,10 @@ void message_callback(SKSE::MessagingInterface::Message* msg)
rlog::trace("SKSE kNewGame message received: type={};"sv, static_cast<uint32_t>(msg->type));
initialize_hud();
break;
case SKSE::MessagingInterface::kPostPostLoad:
rlog::debug("Registering with Trainwreck if it's found...");
register_with_trainwreck();
break;
default: break;
}
}
Expand Down
2 changes: 2 additions & 0 deletions src/renderer/ui_renderer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,8 @@ namespace ui
return d3dTextureFromBuffer(&loadedImg, out_srv, out_width, out_height);
}

size_t rasterizedSVGCount() { return ICON_MAP.size(); }

bool ui_renderer::lazyLoadIcon(std::string name)
{
auto key = std::string(get_icon_key(name));
Expand Down
1 change: 1 addition & 0 deletions src/renderer/ui_renderer.h
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ namespace ui
void drawTextureQuad(ID3D11ShaderResourceView* texture,
const std::array<ImVec2, 4> bounds,
const soulsy::Color color);
size_t rasterizedSVGCount();

// TODO either make this use the fact that it's a class or make it not a class.
class ui_renderer
Expand Down
45 changes: 45 additions & 0 deletions src/util/trains.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
#include "trains.h"

#include "lib.rs.h"
#include "trainwreck.h"
#include "ui_renderer.h"

struct TESForm
{
char padding[0x30];
std::uint32_t formID;
};

void register_with_trainwreck()
{
trainwreck::register_section(TRAINWRECK_SECTION_MODULES,
TRAINWRECK_SECTION_BEFORE,
"SoulsyHUD",
[](auto args)
{
auto log = trainwreck::Log(args->log_context);
log.write_line("Relevant Soulsy data:");
log.with_indent(
[](auto&& log)
{
log.write_line(fmt::format("{} icons loaded", ui::rasterizedSVGCount()));
log.write_line(fmt::format("{} hud items in cache", cache_size()));
});
});

trainwreck::register_decoder(".?AVTESForm@@",
[](auto args)
{
auto log = trainwreck::Log(args->log_context);
const auto form = reinterpret_cast<const TESForm*>(args->object);
try
{
log.write_line(fmt::format("FormID: {:X}", form->formID));
}
catch (...)
{
// requires compiling with /EHa
log.write_line("FormID: <INVALID>");
}
});
}
3 changes: 3 additions & 0 deletions src/util/trains.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#pragma once

void register_with_trainwreck();
199 changes: 199 additions & 0 deletions src/util/trainwreck.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,199 @@
#include <Windows.h>
#include <stddef.h>
#include <stdint.h>

#pragma comment(lib, "Kernel32")

struct trainwreck_string {
const uint8_t *data;
size_t len;
};

struct trainwreck_decoder_callback_args {
uint32_t version;
void *user_context;
void *log_context;
const void *object;
};

struct trainwreck_section_callback_args {
uint32_t version;
void *user_context;
void *log_context;
const EXCEPTION_POINTERS *exception;
};

enum trainwreck_error {
TRAINWRECK_ERROR_OK = 0,

TRAINWRECK_ERROR_REGISTER_SECTION_WHERE_INVALID,
TRAINWRECK_ERROR_REGISTER_SECTION_HOW_INVALID,
TRAINWRECK_ERROR_REGISTER_SECTION_HEADER_INVALID,
TRAINWRECK_ERROR_REGISTER_SECTION_CALLBACK_INVALID,

TRAINWRECK_ERROR_LOG_CONTEXT_INVALID,
TRAINWRECK_ERROR_LOG_LINE_INVALID,

TRAINWRECK_ERROR_REGISTER_DECODER_DECORATED_NAME_INVALID,
TRAINWRECK_ERROR_REGISTER_DECODER_CALLBACK_INVALID,
};

enum trainwreck_section_where {
TRAINWRECK_SECTION_SYSTEM_SPECS = 0,
TRAINWRECK_SECTION_CALL_STACK,
TRAINWRECK_SECTION_REGISTERS,
TRAINWRECK_SECTION_STACK,
TRAINWRECK_SECTION_MODULES,
TRAINWRECK_SECTION_XSE_PLUGINS,
};

enum trainwreck_section_how {
TRAINWRECK_SECTION_BEFORE = 0,
TRAINWRECK_SECTION_AFTER,
};

typedef enum trainwreck_error(__cdecl *trainwreck_log_indent_t)(void *context);
typedef enum trainwreck_error(__cdecl *trainwreck_log_dedent_t)(void *context);
typedef enum trainwreck_error(__cdecl *trainwreck_log_write_line_t)(
void *context, const struct trainwreck_string *line);

typedef void(__cdecl *trainwreck_register_section_callback_t)(
const struct trainwreck_section_callback_args *);
typedef enum trainwreck_error(__cdecl *trainwreck_register_section_t)(
uint32_t register_where, uint32_t register_how,
const struct trainwreck_string *header,
trainwreck_register_section_callback_t callback, void *user_context);

typedef void(__cdecl *trainwreck_register_decoder_callback_t)(
const struct trainwreck_decoder_callback_args *);
typedef enum trainwreck_error(__cdecl *trainwreck_register_decoder_t)(
const struct trainwreck_string *decorated_name,
trainwreck_register_decoder_callback_t callback, void *user_context);

#if __cplusplus > 201606L

#include <functional>
#include <optional>
#include <string_view>

namespace trainwreck {
auto log_indent(void *context) -> std::optional<::trainwreck_error> {
const auto handle = ::GetModuleHandleW(L"trainwreck.dll");
if (handle != NULL) {
const auto proc = ::GetProcAddress(handle, "trainwreck_log_indent");
if (proc != NULL) {
const auto func = reinterpret_cast<::trainwreck_log_indent_t>(proc);
return func(context);
}
}

return std::nullopt;
}

auto log_dedent(void *context) -> std::optional<::trainwreck_error> {
const auto handle = ::GetModuleHandleW(L"trainwreck.dll");
if (handle != NULL) {
const auto proc = ::GetProcAddress(handle, "trainwreck_log_dedent");
if (proc != NULL) {
const auto func = reinterpret_cast<::trainwreck_log_dedent_t>(proc);
return func(context);
}
}

return std::nullopt;
}

auto log_write_line(void *context, std::string_view line)
-> std::optional<::trainwreck_error> {
const auto handle = ::GetModuleHandleW(L"trainwreck.dll");
if (handle != NULL) {
const auto proc = ::GetProcAddress(handle, "trainwreck_log_write_line");
if (proc != NULL) {
const auto func = reinterpret_cast<::trainwreck_log_write_line_t>(proc);
const auto string = ::trainwreck_string{
.data = reinterpret_cast<const uint8_t *>(line.data()),
.len = line.length(),
};
return func(context, &string);
}
}

return std::nullopt;
}

auto register_section(::trainwreck_section_where register_where,
::trainwreck_section_how register_how,
std::string_view header,
::trainwreck_register_section_callback_t callback,
void *user_context = nullptr)
-> std::optional<::trainwreck_error> {
const auto handle = ::GetModuleHandleW(L"trainwreck.dll");
if (handle != NULL) {
const auto proc = ::GetProcAddress(handle, "trainwreck_register_section");
if (proc != NULL) {
const auto func = reinterpret_cast<::trainwreck_register_section_t>(proc);
const auto string = ::trainwreck_string{
.data = reinterpret_cast<const uint8_t *>(header.data()),
.len = header.length(),
};
return func(register_where, register_how, &string, callback,
user_context);
}
}

return std::nullopt;
}

auto register_decoder(std::string_view decorated_name,
::trainwreck_register_decoder_callback_t callback,
void *user_context = nullptr)
-> std::optional<::trainwreck_error> {
const auto handle = ::GetModuleHandleW(L"trainwreck.dll");
if (handle != NULL) {
const auto proc = ::GetProcAddress(handle, "trainwreck_register_decoder");
if (proc != NULL) {
const auto func = reinterpret_cast<::trainwreck_register_decoder_t>(proc);
const auto string = ::trainwreck_string{
.data = reinterpret_cast<const uint8_t *>(decorated_name.data()),
.len = decorated_name.length(),
};
return func(&string, callback, user_context);
}
}

return std::nullopt;
}

class Log {
public:
Log(void *log_context) { this->m_log_context = log_context; }

auto indent() -> std::optional<::trainwreck_error> {
return trainwreck::log_indent(this->m_log_context);
}

auto dedent() -> std::optional<::trainwreck_error> {
return trainwreck::log_dedent(this->m_log_context);
}

auto with_indent(std::function<void(trainwreck::Log &)> callback)
-> std::optional<::trainwreck_error> {
const auto result = this->indent();
if (result.has_value() && *result == ::TRAINWRECK_ERROR_OK) {
callback(*this);
return this->dedent();
} else {
return result;
}
}

auto write_line(std::string_view line) -> std::optional<::trainwreck_error> {
return trainwreck::log_write_line(this->m_log_context, line);
}

private:
void *m_log_context = nullptr;
};
} // namespace trainwreck

#endif

0 comments on commit c9c4ef5

Please sign in to comment.