1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
// Where the client reads and writes, and how it says what it is doing.
//
// Everything lives beside the DLL rather than the game, so the pair can be
// dropped anywhere and still find each other. The log is reopened per line: it
// is low volume, and it means an external `Move-Item` on the file (which the
// deploy script does to rotate it) is picked up immediately instead of writing
// into a handle pointing at the old inode.
#pragma once
#include <windows.h>
#include <cstdio>
#include <string>
namespace lodestone {
/// Set once in DllMain; the module we were loaded as.
inline HMODULE g_module = nullptr;
/// The folder this DLL was loaded from.
inline std::string client_dir() {
wchar_t wide[MAX_PATH * 2] = {};
DWORD n = GetModuleFileNameW(g_module, wide, static_cast<DWORD>(std::size(wide)));
if (n == 0) return ".";
// Trim back to the last separator.
while (n > 0 && wide[n - 1] != L'\\' && wide[n - 1] != L'/') --n;
int bytes = WideCharToMultiByte(CP_UTF8, 0, wide, static_cast<int>(n), nullptr, 0, nullptr, nullptr);
std::string out(static_cast<size_t>(bytes), '\0');
WideCharToMultiByte(CP_UTF8, 0, wide, static_cast<int>(n), out.data(), bytes, nullptr, nullptr);
if (out.empty()) return ".";
return out;
}
inline void log(const std::string& message) {
std::string path = client_dir() + "lodestone.log";
if (FILE* f = std::fopen(path.c_str(), "a")) {
std::fputs(message.c_str(), f);
std::fputc('\n', f);
std::fclose(f);
}
}
/// printf-style, because most log lines want a value in them.
template <typename... Args>
inline void logf(const char* fmt, Args... args) {
char buf[1024];
std::snprintf(buf, sizeof(buf), fmt, args...);
log(buf);
}
} // namespace lodestone