From 0bf3ba7a0a4f4e681731534a02daeafb79bab655 Mon Sep 17 00:00:00 2001 From: rollingrock Date: Wed, 10 Jun 2020 19:54:14 -0500 Subject: [PATCH] first version --- INIReader.h | 439 +++++++++++++++++++++++++++++ IncreaseActorLimit.sln | 31 ++ IncreaseActorLimit.vcxproj | 163 +++++++++++ IncreaseActorLimit.vcxproj.filters | 32 +++ IncreaseActorLimit.vcxproj.user | 4 + exports.def | 3 + main.cpp | 88 ++++++ 7 files changed, 760 insertions(+) create mode 100644 INIReader.h create mode 100644 IncreaseActorLimit.sln create mode 100644 IncreaseActorLimit.vcxproj create mode 100644 IncreaseActorLimit.vcxproj.filters create mode 100644 IncreaseActorLimit.vcxproj.user create mode 100644 exports.def create mode 100644 main.cpp diff --git a/INIReader.h b/INIReader.h new file mode 100644 index 0000000..bbe1b52 --- /dev/null +++ b/INIReader.h @@ -0,0 +1,439 @@ +// Read an INI file into easy-to-access name/value pairs. + +// inih and INIReader are released under the New BSD license (see LICENSE.txt). +// Go to the project home page for more info: +// +// https://github.com/benhoyt/inih +/* inih -- simple .INI file parser + +inih is released under the New BSD license (see LICENSE.txt). Go to the project +home page for more info: + +https://github.com/benhoyt/inih + +*/ + +#ifndef __INI_H__ +#define __INI_H__ + +/* Make this header file easier to include in C++ code */ +#ifdef __cplusplus +extern "C" { +#endif + +#include + + /* Typedef for prototype of handler function. */ + typedef int(*ini_handler)(void* user, const char* section, + const char* name, const char* value); + + /* Typedef for prototype of fgets-style reader function. */ + typedef char* (*ini_reader)(char* str, int num, void* stream); + + /* Parse given INI-style file. May have [section]s, name=value pairs + (whitespace stripped), and comments starting with ';' (semicolon). Section + is "" if name=value pair parsed before any section heading. name:value + pairs are also supported as a concession to Python's configparser. + + For each name=value pair parsed, call handler function with given user + pointer as well as section, name, and value (data only valid for duration + of handler call). Handler should return nonzero on success, zero on error. + + Returns 0 on success, line number of first error on parse error (doesn't + stop on first error), -1 on file open error, or -2 on memory allocation + error (only when INI_USE_STACK is zero). + */ + int ini_parse(const char* filename, ini_handler handler, void* user); + + /* Same as ini_parse(), but takes a FILE* instead of filename. This doesn't + close the file when it's finished -- the caller must do that. */ + int ini_parse_file(FILE* file, ini_handler handler, void* user); + + /* Same as ini_parse(), but takes an ini_reader function pointer instead of + filename. Used for implementing custom or string-based I/O. */ + int ini_parse_stream(ini_reader reader, void* stream, ini_handler handler, + void* user); + + /* Nonzero to allow multi-line value parsing, in the style of Python's + configparser. If allowed, ini_parse() will call the handler with the same + name for each subsequent line parsed. */ +#ifndef INI_ALLOW_MULTILINE +#define INI_ALLOW_MULTILINE 1 +#endif + + /* Nonzero to allow a UTF-8 BOM sequence (0xEF 0xBB 0xBF) at the start of + the file. See http://code.google.com/p/inih/issues/detail?id=21 */ +#ifndef INI_ALLOW_BOM +#define INI_ALLOW_BOM 1 +#endif + + /* Nonzero to allow inline comments (with valid inline comment characters + specified by INI_INLINE_COMMENT_PREFIXES). Set to 0 to turn off and match + Python 3.2+ configparser behaviour. */ +#ifndef INI_ALLOW_INLINE_COMMENTS +#define INI_ALLOW_INLINE_COMMENTS 1 +#endif +#ifndef INI_INLINE_COMMENT_PREFIXES +#define INI_INLINE_COMMENT_PREFIXES ";" +#endif + + /* Nonzero to use stack, zero to use heap (malloc/free). */ +#ifndef INI_USE_STACK +#define INI_USE_STACK 1 +#endif + +/* Stop parsing on first error (default is to keep parsing). */ +#ifndef INI_STOP_ON_FIRST_ERROR +#define INI_STOP_ON_FIRST_ERROR 0 +#endif + +/* Maximum line length for any line in INI file. */ +#ifndef INI_MAX_LINE +#define INI_MAX_LINE 200 +#endif + +#ifdef __cplusplus +} +#endif + +/* inih -- simple .INI file parser + +inih is released under the New BSD license (see LICENSE.txt). Go to the project +home page for more info: + +https://github.com/benhoyt/inih + +*/ + +#if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS) +#define _CRT_SECURE_NO_WARNINGS +#endif + +#include +#include +#include + +#if !INI_USE_STACK +#include +#endif + +#define MAX_SECTION 50 +#define MAX_NAME 50 + +/* Strip whitespace chars off end of given string, in place. Return s. */ +inline static char* rstrip(char* s) +{ + char* p = s + strlen(s); + while (p > s && isspace((unsigned char)(*--p))) + *p = '\0'; + return s; +} + +/* Return pointer to first non-whitespace char in given string. */ +inline static char* lskip(const char* s) +{ + while (*s && isspace((unsigned char)(*s))) + s++; + return (char*)s; +} + +/* Return pointer to first char (of chars) or inline comment in given string, + or pointer to null at end of string if neither found. Inline comment must + be prefixed by a whitespace character to register as a comment. */ +inline static char* find_chars_or_comment(const char* s, const char* chars) +{ +#if INI_ALLOW_INLINE_COMMENTS + int was_space = 0; + while (*s && (!chars || !strchr(chars, *s)) && + !(was_space && strchr(INI_INLINE_COMMENT_PREFIXES, *s))) { + was_space = isspace((unsigned char)(*s)); + s++; + } +#else + while (*s && (!chars || !strchr(chars, *s))) { + s++; + } +#endif + return (char*)s; +} + +/* Version of strncpy that ensures dest (size bytes) is null-terminated. */ +inline static char* strncpy0(char* dest, const char* src, size_t size) +{ + strncpy_s(dest, size, src, _TRUNCATE); + dest[size - 1] = '\0'; + return dest; +} + +/* See documentation in header file. */ +inline int ini_parse_stream(ini_reader reader, void* stream, ini_handler handler, + void* user) +{ + /* Uses a fair bit of stack (use heap instead if you need to) */ +#if INI_USE_STACK + char line[INI_MAX_LINE]; +#else + char* line; +#endif + char section[MAX_SECTION] = ""; + char prev_name[MAX_NAME] = ""; + + char* start; + char* end; + char* name; + char* value; + int lineno = 0; + int error = 0; + +#if !INI_USE_STACK + line = (char*)malloc(INI_MAX_LINE); + if (!line) { + return -2; + } +#endif + + /* Scan through stream line by line */ + while (reader(line, INI_MAX_LINE, stream) != NULL) { + lineno++; + + start = line; +#if INI_ALLOW_BOM + if (lineno == 1 && (unsigned char)start[0] == 0xEF && + (unsigned char)start[1] == 0xBB && + (unsigned char)start[2] == 0xBF) { + start += 3; + } +#endif + start = lskip(rstrip(start)); + + if (*start == ';' || *start == '#') { + /* Per Python configparser, allow both ; and # comments at the + start of a line */ + } +#if INI_ALLOW_MULTILINE + else if (*prev_name && *start && start > line) { + +#if INI_ALLOW_INLINE_COMMENTS + end = find_chars_or_comment(start, NULL); + if (*end) + *end = '\0'; + rstrip(start); +#endif + + /* Non-blank line with leading whitespace, treat as continuation + of previous name's value (as per Python configparser). */ + if (!handler(user, section, prev_name, start) && !error) + error = lineno; + } +#endif + else if (*start == '[') { + /* A "[section]" line */ + end = find_chars_or_comment(start + 1, "]"); + if (*end == ']') { + *end = '\0'; + strncpy0(section, start + 1, sizeof(section)); + *prev_name = '\0'; + } + else if (!error) { + /* No ']' found on section line */ + error = lineno; + } + } + else if (*start) { + /* Not a comment, must be a name[=:]value pair */ + end = find_chars_or_comment(start, "=:"); + if (*end == '=' || *end == ':') { + *end = '\0'; + name = rstrip(start); + value = lskip(end + 1); +#if INI_ALLOW_INLINE_COMMENTS + end = find_chars_or_comment(value, NULL); + if (*end) + *end = '\0'; +#endif + rstrip(value); + + /* Valid name[=:]value pair found, call handler */ + strncpy0(prev_name, name, sizeof(prev_name)); + if (!handler(user, section, name, value) && !error) + error = lineno; + } + else if (!error) { + /* No '=' or ':' found on name[=:]value line */ + error = lineno; + } + } + +#if INI_STOP_ON_FIRST_ERROR + if (error) + break; +#endif + } + +#if !INI_USE_STACK + free(line); +#endif + + return error; +} + +/* See documentation in header file. */ +inline int ini_parse_file(FILE* file, ini_handler handler, void* user) +{ + return ini_parse_stream((ini_reader)fgets, file, handler, user); +} + +/* See documentation in header file. */ +inline int ini_parse(const char* filename, ini_handler handler, void* user) +{ + FILE* file = 0; + int error; + + fopen_s(&file, filename, "r"); + if (!file) + return -1; + error = ini_parse_file(file, handler, user); + fclose(file); + return error; +} + +#endif /* __INI_H__ */ + + +#ifndef __INIREADER_H__ +#define __INIREADER_H__ + +#include +#include +#include + +// Read an INI file into easy-to-access name/value pairs. (Note that I've gone +// for simplicity here rather than speed, but it should be pretty decent.) +class INIReader +{ +public: + // Empty Constructor + INIReader() {}; + + // Construct INIReader and parse given filename. See ini.h for more info + // about the parsing. + INIReader(const std::string& filename); + + // Return the result of ini_parse(), i.e., 0 on success, line number of + // first error on parse error, or -1 on file open error. + int ParseError() const; + + // Return the list of sections found in ini file + const std::set& Sections() const; + + // Get a string value from INI file, returning default_value if not found. + std::string Get(const std::string& section, const std::string& name, + const std::string& default_value) const; + + // Get an integer (long) value from INI file, returning default_value if + // not found or not a valid integer (decimal "1234", "-1234", or hex "0x4d2"). + long GetInteger(const std::string& section, const std::string& name, long default_value) const; + + // Get a real (floating point double) value from INI file, returning + // default_value if not found or not a valid floating point value + // according to strtod(). + double GetReal(const std::string& section, const std::string& name, double default_value) const; + + // Get a boolean value from INI file, returning default_value if not found or if + // not a valid true/false value. Valid true values are "true", "yes", "on", "1", + // and valid false values are "false", "no", "off", "0" (not case sensitive). + bool GetBoolean(const std::string& section, const std::string& name, bool default_value) const; + +protected: + int _error; + std::map _values; + std::set _sections; + static std::string MakeKey(const std::string& section, const std::string& name); + static int ValueHandler(void* user, const char* section, const char* name, + const char* value); +}; + +#endif // __INIREADER_H__ + + +#ifndef __INIREADER__ +#define __INIREADER__ + +#include +#include +#include + +inline INIReader::INIReader(const std::string& filename) +{ + _error = ini_parse(filename.c_str(), ValueHandler, this); +} + +inline int INIReader::ParseError() const +{ + return _error; +} + +inline const std::set& INIReader::Sections() const +{ + return _sections; +} + +inline std::string INIReader::Get(const std::string& section, const std::string& name, const std::string& default_value) const +{ + std::string key = MakeKey(section, name); + return _values.count(key) ? _values.at(key) : default_value; +} + +inline long INIReader::GetInteger(const std::string& section, const std::string& name, long default_value) const +{ + std::string valstr = Get(section, name, ""); + const char* value = valstr.c_str(); + char* end; + // This parses "1234" (decimal) and also "0x4D2" (hex) + long n = strtol(value, &end, 0); + return end > value ? n : default_value; +} + +inline double INIReader::GetReal(const std::string& section, const std::string& name, double default_value) const +{ + std::string valstr = Get(section, name, ""); + const char* value = valstr.c_str(); + char* end; + double n = strtod(value, &end); + return end > value ? n : default_value; +} + +inline bool INIReader::GetBoolean(const std::string& section, const std::string& name, bool default_value) const +{ + std::string valstr = Get(section, name, ""); + // Convert to lower case to make string comparisons case-insensitive + std::transform(valstr.begin(), valstr.end(), valstr.begin(), ::tolower); + if (valstr == "true" || valstr == "yes" || valstr == "on" || valstr == "1") + return true; + else if (valstr == "false" || valstr == "no" || valstr == "off" || valstr == "0") + return false; + else + return default_value; +} + +inline std::string INIReader::MakeKey(const std::string& section, const std::string& name) +{ + std::string key = section + "=" + name; + // Convert to lower case to make section/name lookups case-insensitive + std::transform(key.begin(), key.end(), key.begin(), ::tolower); + return key; +} + +inline int INIReader::ValueHandler(void* user, const char* section, const char* name, + const char* value) +{ + INIReader* reader = (INIReader*)user; + std::string key = MakeKey(section, name); + if (reader->_values[key].size() > 0) + reader->_values[key] += "\n"; + reader->_values[key] += value; + reader->_sections.insert(section); + return 1; +} + +#endif // __INIREADER__ \ No newline at end of file diff --git a/IncreaseActorLimit.sln b/IncreaseActorLimit.sln new file mode 100644 index 0000000..8f6f780 --- /dev/null +++ b/IncreaseActorLimit.sln @@ -0,0 +1,31 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.30114.105 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "IncreaseActorLimit", "IncreaseActorLimit.vcxproj", "{F593EA95-9ACF-4C1D-969E-BA473824B02D}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {F593EA95-9ACF-4C1D-969E-BA473824B02D}.Debug|x64.ActiveCfg = Debug|x64 + {F593EA95-9ACF-4C1D-969E-BA473824B02D}.Debug|x64.Build.0 = Debug|x64 + {F593EA95-9ACF-4C1D-969E-BA473824B02D}.Debug|x86.ActiveCfg = Debug|Win32 + {F593EA95-9ACF-4C1D-969E-BA473824B02D}.Debug|x86.Build.0 = Debug|Win32 + {F593EA95-9ACF-4C1D-969E-BA473824B02D}.Release|x64.ActiveCfg = Release|x64 + {F593EA95-9ACF-4C1D-969E-BA473824B02D}.Release|x64.Build.0 = Release|x64 + {F593EA95-9ACF-4C1D-969E-BA473824B02D}.Release|x86.ActiveCfg = Release|Win32 + {F593EA95-9ACF-4C1D-969E-BA473824B02D}.Release|x86.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {4108465B-8FCA-45AE-9C3F-FAF646D55265} + EndGlobalSection +EndGlobal diff --git a/IncreaseActorLimit.vcxproj b/IncreaseActorLimit.vcxproj new file mode 100644 index 0000000..4044c37 --- /dev/null +++ b/IncreaseActorLimit.vcxproj @@ -0,0 +1,163 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 16.0 + Win32Proj + {f593ea95-9acf-4c1d-969e-ba473824b02d} + IncreaseActorLimit + 10.0 + + + + Application + true + v142 + Unicode + + + Application + false + v142 + true + Unicode + + + Application + true + v142 + Unicode + + + DynamicLibrary + false + v142 + true + Unicode + + + + + + + + + + + + + + + + + + + + + true + + + false + + + true + + + false + ..\CommonLibVR\include;..\sksevr\src\sksevr;..\sksevr\src;$(SolutionDir)lib;$(SolutionDir)include;$(VC_IncludePath);$(WindowsSDK_IncludePath);$(SolutionDir) + lib;$(VC_LibraryPath_x64);$(WindowsSDK_LibraryPath_x64) + + + + 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) + false + stdcpp17 + CompileAsCpp + common/IPrefix.h;ForceInclude.h;SKSE/Logger.h + false + true + MultiThreaded + + + Console + true + true + true + common_vc14.lib;sksevr_1_4_15.lib;skse64_common.lib;CommonLibVR.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + exports.def + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/IncreaseActorLimit.vcxproj.filters b/IncreaseActorLimit.vcxproj.filters new file mode 100644 index 0000000..0d9fc82 --- /dev/null +++ b/IncreaseActorLimit.vcxproj.filters @@ -0,0 +1,32 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;c++;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 + + + \ No newline at end of file diff --git a/IncreaseActorLimit.vcxproj.user b/IncreaseActorLimit.vcxproj.user new file mode 100644 index 0000000..88a5509 --- /dev/null +++ b/IncreaseActorLimit.vcxproj.user @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/exports.def b/exports.def new file mode 100644 index 0000000..868425c --- /dev/null +++ b/exports.def @@ -0,0 +1,3 @@ +EXPORTS +SKSEPlugin_Query +SKSEPlugin_Load \ No newline at end of file diff --git a/main.cpp b/main.cpp new file mode 100644 index 0000000..a1746ec --- /dev/null +++ b/main.cpp @@ -0,0 +1,88 @@ +#include +#include "SKSE/API.h" +#include "SKSE/Interfaces.h" +#include "SKSE/Logger.h" +#include "INIReader.h" + +int numActors; + +REL::Offset firstLocaction(0x6fbfb0); +REL::Offset secondLocaction(0x6fbfbc); + + +bool loadConfig() { + INIReader in(".\\Data\\SKSE\\plugins\\IncreaseActorLimit.ini"); + + if (in.ParseError() < 0) { + _MESSAGE("ERROR: cannot read IncreaseActorLimit.ini"); + return false; + } + + numActors = in.GetInteger("IncreaseActorLimit", "NumberOfActors", 128); + + return true; +} + +bool doFix() { + + if (numActors != 128) { + UInt8 x = numActors & 0xFF; + SKSE::SafeWrite8(firstLocaction.GetAddress() + 1, x); + SKSE::SafeWrite8(secondLocaction.GetAddress() + 1, x); + + _MESSAGE("patched offsets %016I64X and %016I64X", firstLocaction.GetOffset(), secondLocaction.GetOffset()); + return true; + } + + _MESSAGE("selected value is default. no need to patch"); + + return true; +} + +extern "C" { + bool SKSEPlugin_Query(const SKSE::QueryInterface* a_skse, SKSE::PluginInfo* a_info) + { + SKSE::Logger::OpenRelative(FOLDERID_Documents, R"(\\My Games\\Skyrim VR\\SKSE\\IncreaseActorLimit.log)"); + SKSE::Logger::SetPrintLevel(SKSE::Logger::Level::kDebugMessage); + SKSE::Logger::SetFlushLevel(SKSE::Logger::Level::kDebugMessage); + SKSE::Logger::TrackTrampolineStats(true); + SKSE::Logger::UseLogStamp(true); + + a_info->infoVersion = SKSE::PluginInfo::kVersion; + a_info->name = "IncreaseActorLimit"; + a_info->version = 1; + + if (a_skse->IsEditor()) + { + _ERROR("loaded in editor, marking as incompatible"); + + return false; + } + + auto ver = a_skse->RuntimeVersion(); + if (ver <= SKSE::RUNTIME_VR_1_4_15) + { + _FATALERROR("Unsupported runtime version %s!\n", ver.GetString().c_str()); + return false; + } + return true; + } + + bool SKSEPlugin_Load(const SKSE::LoadInterface* a_skse) + { + if (!SKSE::Init(a_skse)) + { + _FATALERROR("Cannot INIT SKSE CommonLib!!!!"); + return false; + } + + if (!loadConfig()) { + return false; + } + _MESSAGE("settings ini successfully read in"); + + doFix(); + _MESSAGE("[MESSAGE] IncreaseActorLimit loaded"); + return true; + } +}; \ No newline at end of file