├── src ├── exports.def ├── PapyrusMCM.h ├── MCMSerialization.h ├── MCMTranslator.h ├── Globals.h ├── MCMInput.h ├── ScaleformMCM.h ├── MCMSerialization.cpp ├── SettingStore.h ├── f4mcm.vcxproj.filters ├── Utils.h ├── Globals.cpp ├── Config.h ├── MCMTranslator.cpp ├── PapyrusMCM.cpp ├── MCMKeybinds.h ├── MCMInput.cpp ├── Utils.cpp ├── MCM.cpp ├── f4mcm.vcxproj ├── SettingStore.cpp ├── json │ ├── json-forwards.h │ └── json.h ├── MCMKeybinds.cpp └── ScaleformMCM.cpp ├── dist └── Data │ ├── Scripts │ ├── MCM.pex │ └── Source │ │ └── User │ │ └── MCM.psc │ ├── Interface │ ├── MCM.swf │ └── Translations │ │ └── MCM_en.txt │ └── MCM │ ├── Config │ └── MCM │ │ └── settings.ini │ └── Settings │ └── readme.txt ├── .gitmodules ├── README.md ├── appveyor.yml ├── .gitignore └── changelog.txt /src/exports.def: -------------------------------------------------------------------------------- 1 | LIBRARY "mcm" 2 | EXPORTS 3 | F4SEPlugin_Query 4 | F4SEPlugin_Load -------------------------------------------------------------------------------- /dist/Data/Scripts/MCM.pex: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reg2k/f4mcm/HEAD/dist/Data/Scripts/MCM.pex -------------------------------------------------------------------------------- /dist/Data/Interface/MCM.swf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reg2k/f4mcm/HEAD/dist/Data/Interface/MCM.swf -------------------------------------------------------------------------------- /dist/Data/Interface/Translations/MCM_en.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reg2k/f4mcm/HEAD/dist/Data/Interface/Translations/MCM_en.txt -------------------------------------------------------------------------------- /src/PapyrusMCM.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | struct StaticFunctionTag; 4 | class VirtualMachine; 5 | 6 | namespace PapyrusMCM 7 | { 8 | void RegisterFuncs(VirtualMachine* vm); 9 | } 10 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "src/rva"] 2 | path = src/rva 3 | url = https://github.com/reg2k/rva.git 4 | [submodule "tools/build-tools"] 5 | path = tools/build-tools 6 | url = https://github.com/reg2k/f4se-build-tools.git 7 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Fallout 4 Mod Configuration Menu 2 | 3 | The Mod Configuration Menu is a settings page for mods! 4 | 5 | It provides a central location for mod configuration, accessible via the Pause menu. 6 | 7 | Mod page: https://www.nexusmods.com/fallout4/mods/21497/ 8 | -------------------------------------------------------------------------------- /src/MCMSerialization.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "f4se/PluginAPI.h" 3 | 4 | namespace MCMSerialization 5 | { 6 | void RevertCallback(const F4SESerializationInterface * intfc); 7 | void LoadCallback(const F4SESerializationInterface * intfc); 8 | void SaveCallback(const F4SESerializationInterface * intfc); 9 | } -------------------------------------------------------------------------------- /src/MCMTranslator.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // This file is adapted from f4se/Translation.h. 4 | 5 | class BSScaleformTranslator; 6 | 7 | namespace MCMTranslator 8 | { 9 | void LoadTranslations(BSScaleformTranslator * translator); 10 | bool ParseTranslation(BSScaleformTranslator * translator, std::string modName, std::string langCode); 11 | } 12 | -------------------------------------------------------------------------------- /dist/Data/MCM/Config/MCM/settings.ini: -------------------------------------------------------------------------------- 1 | ; This file contains MCM setting defaults. 2 | ; Do not modify this file - your changes may be lost if this file is updated in an MCM update. 3 | ; To make changes, make a copy of this file in Data\MCM\Settings\MCM.ini - values defined there will override those defined in this file and are safe against file updates. 4 | 5 | [Main] 6 | iPosition=1 7 | sOrder= 8 | -------------------------------------------------------------------------------- /dist/Data/MCM/Settings/readme.txt: -------------------------------------------------------------------------------- 1 | This folder contains user settings (also known as ModSettings) for MCM-supported mods. 2 | 3 | ModSetting changes are stored as per-mod INI files in this folder. These files are automatically created by the MCM in this folder when a setting is changed from the default. 4 | 5 | Settings stored in this folder are safe against mod updates and will not be lost should a mod introduce new ModSettings. Values stored in this folder will always override the default setting value defined by the mod author. -------------------------------------------------------------------------------- /src/Globals.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "rva/RVA.h" 3 | 4 | class BSScaleformManager; 5 | class MenuControls; 6 | class UI; 7 | class InputManager; 8 | class GameVM; 9 | class DataHandler; 10 | 11 | namespace G 12 | { 13 | void Init(); 14 | extern RVA scaleformManager; 15 | extern RVA menuControls; 16 | extern RVA ui; 17 | extern RVA inputMgr; 18 | extern RVA gameVM; 19 | extern RVA dataHandler; 20 | } -------------------------------------------------------------------------------- /src/MCMInput.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | #include "f4se/GameInput.h" 5 | 6 | class MCMInput : public BSInputEventUser 7 | { 8 | public: 9 | static MCMInput& GetInstance() { 10 | static MCMInput instance; 11 | return instance; 12 | } 13 | 14 | MCMInput(MCMInput const&) = delete; 15 | void operator=(MCMInput const&) = delete; 16 | 17 | void RegisterForInput(bool bRegister); 18 | 19 | // Input Handlers 20 | virtual void OnButtonEvent(ButtonEvent * inputEvent); 21 | 22 | private: 23 | MCMInput() : BSInputEventUser(true) { } 24 | }; 25 | 26 | -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | image: Visual Studio 2017 2 | 3 | environment: 4 | PYTHON: C:\Python36-x64 5 | 6 | init: 7 | - git config --global core.autocrlf true 8 | 9 | install: 10 | - git submodule update --init --recursive 11 | 12 | build_script: 13 | - set PATH=%PYTHON%;%PYTHON%\Scripts;%PATH% 14 | - call "C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Auxiliary\Build\vcvars64.bat" 15 | - python --version 16 | - python build.py 17 | 18 | artifacts: 19 | - path: build\x64\Release\*.dll 20 | name: DLL only 21 | - path: build\*.zip 22 | name: Packaged 23 | -------------------------------------------------------------------------------- /src/ScaleformMCM.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | class GFxMovieRoot; 4 | class GFxMovieView; 5 | class GFxValue; 6 | 7 | struct KeybindInfo; 8 | 9 | namespace ScaleformMCM 10 | { 11 | bool RegisterScaleform(GFxMovieView* view, GFxValue* f4se_root); 12 | void RegisterFuncs(GFxValue* codeObj, GFxMovieRoot* movieRoot); 13 | 14 | void RegisterForInput(bool bRegister); 15 | void ProcessKeyEvent(UInt32 keyCode, bool isDown); 16 | void ProcessUserEvent(const char* controlName, bool isDown, int deviceType); 17 | 18 | void SetKeybindInfo(KeybindInfo ki, GFxMovieRoot* movieRoot, GFxValue* kiValue); 19 | void RefreshMenu(); 20 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ### Build Artifacts ### 2 | build/ 3 | 4 | ### Visual Studio ### 5 | 6 | # User-specific files 7 | *.suo 8 | *.user 9 | *.userosscache 10 | *.sln.docstates 11 | 12 | # User-specific files 13 | *.userprefs 14 | 15 | # Build results 16 | [Dd]ebug/ 17 | [Dd]ebugPublic/ 18 | [Rr]elease/ 19 | [Rr]eleases/ 20 | x64/ 21 | x86/ 22 | bld/ 23 | [Bb]in/ 24 | [Oo]bj/ 25 | [Ll]og/ 26 | 27 | # Visual Studio 2015 cache/options directory 28 | .vs/ 29 | 30 | *_i.c 31 | *_p.c 32 | *_i.h 33 | *.ilk 34 | *.meta 35 | *.obj 36 | *.pch 37 | *.pdb 38 | *.pgc 39 | *.pgd 40 | *.rsp 41 | *.sbr 42 | *.tlb 43 | *.tli 44 | *.tlh 45 | *.tmp 46 | *.tmp_proj 47 | *.log 48 | *.vspscc 49 | *.vssscc 50 | .builds 51 | *.pidb 52 | *.svclog 53 | *.scc 54 | 55 | # Visual C++ cache files 56 | ipch/ 57 | *.aps 58 | *.ncb 59 | *.opendb 60 | *.opensdf 61 | *.sdf 62 | *.cachefile 63 | *.VC.db 64 | *.VC.VC.opendb 65 | 66 | # Visual Studio cache files 67 | # files ending in .cache can be ignored 68 | *.[Cc]ache 69 | # but keep track of directories ending in .cache 70 | !*.[Cc]ache/ 71 | -------------------------------------------------------------------------------- /src/MCMSerialization.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "json/json.h" 4 | 5 | #include "MCMSerialization.h" 6 | #include "MCMKeybinds.h" 7 | 8 | const char* KEYBIND_LOCATION = "Data\\MCM\\Settings\\Keybinds.json"; 9 | 10 | namespace MCMSerialization 11 | { 12 | enum SaveVersion 13 | { 14 | kVersion_1, 15 | kCurrentVersion = kVersion1 16 | }; 17 | 18 | void RevertCallback(const F4SESerializationInterface * intfc) 19 | { 20 | _DMESSAGE("Clearing MCM co-save internal state."); 21 | g_keybindManager.Clear(); 22 | } 23 | 24 | void LoadCallback(const F4SESerializationInterface * intfc) 25 | { 26 | _DMESSAGE("Loading MCM data."); 27 | 28 | LARGE_INTEGER countStart, countEnd, frequency; 29 | QueryPerformanceCounter(&countStart); 30 | QueryPerformanceFrequency(&frequency); 31 | 32 | // Load keybind registrations. 33 | std::ifstream file(KEYBIND_LOCATION); 34 | std::stringstream ss; 35 | if (file.is_open()) { 36 | ss << file.rdbuf(); 37 | file.close(); 38 | g_keybindManager.FromJSON(ss.str()); 39 | } else { 40 | _MESSAGE("Keybind storage could not be opened or does not exist."); 41 | } 42 | 43 | QueryPerformanceCounter(&countEnd); 44 | _MESSAGE("Elapsed: %llu ms.", (countEnd.QuadPart - countStart.QuadPart) / (frequency.QuadPart / 1000)); 45 | } 46 | 47 | void SaveCallback(const F4SESerializationInterface * intfc) 48 | { 49 | g_keybindManager.CommitKeybinds(); 50 | } 51 | 52 | } -------------------------------------------------------------------------------- /src/SettingStore.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "f4se/GameSettings.h" 6 | 7 | //struct ModSetting { 8 | // char* settingName; 9 | // union { 10 | // float floatValue; 11 | // bool boolValue; 12 | // SInt32 intValue; 13 | // char* strValue; 14 | // }; 15 | //}; 16 | 17 | // Singleton 18 | class SettingStore 19 | { 20 | public: 21 | static SettingStore& GetInstance() { 22 | static SettingStore instance; 23 | return instance; 24 | } 25 | void ReadSettings(); 26 | 27 | SInt32 GetModSettingInt(std::string modName, std::string settingName); 28 | void SetModSettingInt(std::string modName, std::string settingName, SInt32 newValue); 29 | 30 | bool GetModSettingBool(std::string modName, std::string settingName); 31 | void SetModSettingBool(std::string modName, std::string settingName, bool newValue); 32 | 33 | float GetModSettingFloat(std::string modName, std::string settingName); 34 | void SetModSettingFloat(std::string modName, std::string settingName, float newValue); 35 | 36 | char* GetModSettingString(std::string modName, std::string settingName); 37 | void SetModSettingString(std::string modName, std::string settingName, const char* newValue); 38 | 39 | private: 40 | SettingStore(); 41 | std::unordered_map m_settingStore; 42 | 43 | bool ReadINI(std::string modName, std::string iniLocation); 44 | void LoadDefaults(); 45 | void LoadUserSettings(); 46 | 47 | Setting* GetModSetting(std::string modName, std::string settingName); 48 | void RegisterModSetting(std::string modName, std::string settingName, std::string settingValue); 49 | void CommitModSetting(std::string modName, Setting* modSetting); 50 | 51 | public: 52 | // Get rid of unwanted constructors 53 | SettingStore(SettingStore const&) = delete; 54 | void operator=(SettingStore const&) = delete; 55 | }; 56 | 57 | -------------------------------------------------------------------------------- /src/f4mcm.vcxproj.filters: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | json 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | rva\sscan 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | json 29 | 30 | 31 | json 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | rva 40 | 41 | 42 | rva\sscan 43 | 44 | 45 | 46 | 47 | {a2a23ba1-2eef-4627-ab1c-ba941460140c} 48 | 49 | 50 | {9f973d34-d428-4c55-9f2a-bae76e74ca67} 51 | 52 | 53 | {94498cb7-5cf5-4b3e-b89b-db40ceaac834} 54 | 55 | 56 | -------------------------------------------------------------------------------- /src/Utils.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Globals.h" 4 | #include "f4se/GameForms.h" 5 | #include "f4se/PapyrusValue.h" 6 | #include "f4se/PapyrusVM.h" 7 | 8 | namespace MCMUtils { 9 | // 40 10 | struct PropertyInfo { 11 | BSFixedString scriptName; // 00 12 | BSFixedString propertyName; // 08 13 | UInt64 unk10; // 10 14 | void* unk18; // 18 15 | void* unk20; // 20 16 | void* unk28; // 28 17 | SInt32 index; // 30 -1 if not found 18 | UInt32 unk34; // 34 19 | BSFixedString unk38; // 38 20 | }; 21 | STATIC_ASSERT(offsetof(PropertyInfo, index) == 0x30); 22 | STATIC_ASSERT(sizeof(PropertyInfo) == 0x40); 23 | 24 | // Form Management 25 | TESForm * GetFormFromIdentifier(const std::string & identifier); 26 | std::string GetIdentifierFromForm(const TESForm & form); 27 | std::string GetIdentifierFromFormID(UInt32 formID); 28 | 29 | // Console Commands 30 | void ExecuteCommand(const char* cmd); 31 | 32 | // MCM Operation 33 | void DisableProcessUserEvent(bool disable); 34 | 35 | // Papyrus Properties 36 | void GetPropertyInfo(VMObjectTypeInfo* objectTypeInfo, PropertyInfo* outInfo, BSFixedString* propertyName); 37 | bool GetPropertyValue(const char* formIdentifier, const char* scriptName, const char* propertyName, VMValue* valueOut); 38 | bool SetPropertyValue(const char* formIdentifier, const char* scriptName, const char* propertyName, VMValue* valueIn); 39 | 40 | // Utilities 41 | template 42 | T* GetOffsetPtr(const void * baseObject, int offset) 43 | { 44 | return reinterpret_cast((uintptr_t)baseObject + offset); 45 | } 46 | 47 | BSFixedString GetDescription(TESForm * thisForm); 48 | 49 | class VMScript 50 | { 51 | public: 52 | VMScript(TESForm* form, const char* scriptName = "ScriptObject") { 53 | if (!scriptName || scriptName[0] == '\0') scriptName = "ScriptObject"; 54 | VirtualMachine* vm = (*G::gameVM)->m_virtualMachine; 55 | UInt64 handle = vm->GetHandlePolicy()->Create(form->formType, form); 56 | vm->GetObjectIdentifier(handle, scriptName, 1, &m_identifier, 0); 57 | } 58 | 59 | ~VMScript() { 60 | if (m_identifier && !m_identifier->DecrementLock()) 61 | m_identifier->Destroy(); 62 | } 63 | 64 | VMIdentifier* m_identifier = nullptr; 65 | }; 66 | } -------------------------------------------------------------------------------- /dist/Data/Scripts/Source/User/MCM.psc: -------------------------------------------------------------------------------- 1 | Scriptname MCM Native Hidden 2 | 3 | ; Checks to see whether the MCM is installed. 4 | bool Function IsInstalled() native global 5 | 6 | ; Returns the version code of the MCM. This value is incremented for every public release of MCM. 7 | int Function GetVersionCode() native global 8 | 9 | ; Refreshes currently displayed values in the MCM menu if it is currently open. 10 | ; Call this if you have changed values in response to a OnMCMSettingChange event. 11 | Function RefreshMenu() native global 12 | 13 | ;----------------- 14 | ; Mod Settings 15 | ;----------------- 16 | 17 | ; Obtains the value of a mod setting. 18 | int Function GetModSettingInt(string asModName, string asSetting) native global 19 | bool Function GetModSettingBool(string asModName, string asSetting) native global 20 | float Function GetModSettingFloat(string asModName, string asSetting) native global 21 | string Function GetModSettingString(string asModName, string asSetting) native global 22 | 23 | ; Sets the value of a mod setting. 24 | Function SetModSettingInt(string asModName, string asSettingName, int aiValue) native global 25 | Function SetModSettingBool(string asModName, string asSettingName, bool abValue) native global 26 | Function SetModSettingFloat(string asModName, string asSettingName, float afValue) native global 27 | Function SetModSettingString(string asModName, string asSettingName, string asValue) native global 28 | 29 | ;----------------- 30 | ; Events 31 | ;----------------- 32 | 33 | ; Events dispatched by the MCM: 34 | ; - OnMCMSettingChange(string modName, string id) 35 | ; - OnMCMMenuOpen(string modName) 36 | ; - OnMCMMenuClose(string modName) 37 | 38 | ; To register for MCM events, use RegisterForExternalEvent. 39 | ; To receive events from a specified mod only, use the pipe symbol (|) followed by the mod name. 40 | 41 | ; e.g. RegisterForExternalEvent("OnMCMSettingChange|MyModName", "OnMCMSettingChange") 42 | 43 | ;------------------------------ 44 | ; Example Event Registration 45 | ;------------------------------ 46 | 47 | ; Event OnInit() 48 | ; RegisterForExternalEvent("OnMCMSettingChange|MyModName", "OnMCMSettingChange") 49 | ; EndEvent 50 | 51 | ; Function OnMCMSettingChange(string modName, string id) 52 | ; If (modName == "MyModName") 53 | ; If (id == "MyControlID") 54 | ; Debug.Notification("MyControlID's value was changed!") 55 | ; EndIf 56 | ; EndIf 57 | ; EndFunction 58 | -------------------------------------------------------------------------------- /src/Globals.cpp: -------------------------------------------------------------------------------- 1 | #include "Globals.h" 2 | #include "f4se_common/Relocation.h" 3 | #include "f4se_common/f4se_version.h" 4 | 5 | #define GET_RVA(relocPtr) relocPtr.GetUIntPtr() - RelocationManager::s_baseAddr 6 | 7 | /* 8 | This file makes globals version-independent. 9 | 10 | Initialization order is important for this file. 11 | 12 | Since RelocPtrs are static globals with constructors they are initialized during the dynamic initialization phase. 13 | Static initialization order is undefined for variables in different translation units, so it is not possible to deterministically obtain the value of a RelocPtr during static init. 14 | 15 | Initialization must thus be done explicitly: 16 | Call G::Init() in the plugin load routine before calling RVAManager::UpdateAddresses(). 17 | 18 | Doing so ensures that all RelocPtrs have been initialized and can be used to initialize an RVA. 19 | */ 20 | 21 | #include "f4se/GameData.h" 22 | #include "f4se/GameInput.h" 23 | #include "f4se/GameMenus.h" 24 | #include "f4se/PapyrusVM.h" 25 | #include "f4se/ScaleformLoader.h" 26 | 27 | namespace G 28 | { 29 | RVA scaleformManager; 30 | RVA menuControls; 31 | RVA ui; 32 | RVA inputMgr; 33 | RVA gameVM; 34 | RVA dataHandler; 35 | 36 | void Init() 37 | { 38 | scaleformManager = RVA (GET_RVA(g_scaleformManager), "48 8B 0D ? ? ? ? 48 8D 05 ? ? ? ? 48 8B D3", 0, 3, 7); 39 | menuControls = RVA (GET_RVA(g_menuControls), "48 8B 0D ? ? ? ? E8 ? ? ? ? 80 3D ? ? ? ? ? 0F B6 F8", 0, 3, 7); 40 | ui = RVA (GET_RVA(g_ui), "48 8B 0D ? ? ? ? BA ? ? ? ? 8B 1C 16", 0, 3, 7); 41 | inputMgr = RVA (GET_RVA(g_inputMgr), "48 83 3D ? ? ? ? ? 74 3F 48 83 C1 40", 0, 3, 8); 42 | gameVM = RVA (GET_RVA(g_gameVM), "4C 8B 05 ? ? ? ? 48 8B F9", 0, 3, 7); 43 | dataHandler = RVA (GET_RVA(g_dataHandler), "48 8B 05 ? ? ? ? 8B 13", 0, 3, 7); 44 | } 45 | 46 | // mov rcx, cs:qq_g_scaleformManager 47 | // mov rcx, cs:qq_g_menuControls 48 | // mov rcx, cs:qq_g_ui 49 | // cmp cs:qq_g_inputMgr, 0 50 | // mov r8, cs:qq_g_gameVM 51 | // mov rax, cs:qq_g_dataHandler 52 | } -------------------------------------------------------------------------------- /src/Config.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "f4se_common/f4se_version.h" 3 | 4 | //----------------------- 5 | // Plugin Information 6 | //----------------------- 7 | #define PLUGIN_VERSION 9 8 | #define PLUGIN_VERSION_STRING "1.40" 9 | #define PLUGIN_NAME_SHORT "F4MCM" 10 | #define PLUGIN_NAME_LONG "Mod Configuration Menu" 11 | #define SUPPORTED_RUNTIME_VERSION CURRENT_RELEASE_RUNTIME 12 | #define MINIMUM_RUNTIME_VERSION RUNTIME_VERSION_1_9_4 13 | 14 | // Addresses 15 | #if SUPPORTED_RUNTIME_VERSION == RUNTIME_VERSION_1_10_98 16 | #define Addr_ExecuteCommand 0x0125B380 17 | #define Addr_ProcessUserEvent_Check 0x0210F69C 18 | #define Addr_GetPropertyInfo 0x02718920 19 | 20 | #elif SUPPORTED_RUNTIME_VERSION == RUNTIME_VERSION_1_10_89 21 | #define Addr_ExecuteCommand 0x0125B340 22 | #define Addr_ProcessUserEvent_Check 0x0210F65C 23 | #define Addr_GetPropertyInfo 0x027188E0 24 | 25 | #elif SUPPORTED_RUNTIME_VERSION == RUNTIME_VERSION_1_10_82 26 | #define Addr_ExecuteCommand 0x0125B2E0 27 | #define Addr_ProcessUserEvent_Check 0x0210F5FC 28 | #define Addr_GetPropertyInfo 0x02718860 29 | 30 | #elif SUPPORTED_RUNTIME_VERSION == RUNTIME_VERSION_1_10_75 31 | #define Addr_ExecuteCommand 0x0125B2E0 32 | #define Addr_ProcessUserEvent_Check 0x0210F58C 33 | #define Addr_GetPropertyInfo 0x027187F0 34 | 35 | #elif SUPPORTED_RUNTIME_VERSION == RUNTIME_VERSION_1_10_64 36 | #define Addr_ExecuteCommand 0x0125B320 37 | #define Addr_ProcessUserEvent_Check 0x0210F5CC 38 | #define Addr_GetPropertyInfo 0x02718830 39 | 40 | #elif SUPPORTED_RUNTIME_VERSION == RUNTIME_VERSION_1_10_50 41 | #define Addr_ExecuteCommand 0x0125AF00 42 | #define Addr_ProcessUserEvent_Check 0x0210F1AC 43 | #define Addr_GetPropertyInfo 0x02718460 44 | 45 | #elif SUPPORTED_RUNTIME_VERSION == RUNTIME_VERSION_1_10_40 46 | #define Addr_ExecuteCommand 0x0125AE40 47 | #define Addr_ProcessUserEvent_Check 0x0210F73C 48 | #define Addr_GetPropertyInfo 0x02718690 49 | 50 | #elif SUPPORTED_RUNTIME_VERSION == RUNTIME_VERSION_1_10_26 51 | #define Addr_ExecuteCommand 0x012594F0 52 | #define Addr_ProcessUserEvent_Check 0x0210DDDC 53 | #define Addr_GetPropertyInfo 0x02707630 54 | 55 | #elif SUPPORTED_RUNTIME_VERSION == RUNTIME_VERSION_1_10_20 56 | #define Addr_ExecuteCommand 0x01259430 57 | #define Addr_ProcessUserEvent_Check 0x0210DD1C 58 | #define Addr_GetPropertyInfo 0x02707500 59 | 60 | #elif SUPPORTED_RUNTIME_VERSION == RUNTIME_VERSION_1_9_4 61 | #define Addr_ExecuteCommand 0x012416F0 62 | #define Addr_ProcessUserEvent_Check 0x020E745C 63 | #define Addr_GetPropertyInfo 0x026A52E0 64 | 65 | #endif -------------------------------------------------------------------------------- /src/MCMTranslator.cpp: -------------------------------------------------------------------------------- 1 | #include "MCMTranslator.h" 2 | 3 | #include "common/IFileStream.h" 4 | #include 5 | #include 6 | #include "f4se/GameStreams.h" 7 | #include "f4se/GameSettings.h" 8 | 9 | #include "f4se/ScaleformState.h" 10 | #include "f4se/ScaleformTranslator.h" 11 | 12 | // This file is adapted from f4se/Translation.cpp. 13 | 14 | namespace MCMTranslator 15 | { 16 | void LoadTranslations(BSScaleformTranslator * translator) 17 | { 18 | Setting * setting = GetINISetting("sLanguage:General"); 19 | 20 | // Load EN strings first to ensure that no strings are unsubstituted if the locale-specific translation is not present. 21 | ParseTranslation(translator, "mcm", "en"); 22 | 23 | if (strcmp(setting->data.s, "en") != 0) { 24 | ParseTranslation(translator, "mcm", setting->data.s); 25 | } 26 | } 27 | 28 | bool ParseTranslation(BSScaleformTranslator * translator, std::string modName, std::string langCode) 29 | { 30 | std::string filePath = "Interface\\Translations\\"; 31 | filePath += modName; 32 | filePath += "_"; 33 | filePath += langCode; 34 | filePath += ".txt"; 35 | 36 | BSResourceNiBinaryStream fileStream(filePath.c_str()); 37 | if (!fileStream.IsValid()) { 38 | _WARNING("Warning: No translation file available. Locale: %s", langCode.c_str()); 39 | return false; 40 | } 41 | 42 | // Check if file is empty, if not check if the BOM is UTF-16 43 | UInt16 bom = 0; 44 | UInt32 ret = fileStream.Read(&bom, sizeof(UInt16)); 45 | if (ret == 0) { 46 | _MESSAGE("Empty translation file."); 47 | return false; 48 | } 49 | if (bom != 0xFEFF) { 50 | _MESSAGE("BOM Error, file must be encoded in UCS-2 LE."); 51 | return false; 52 | } 53 | 54 | while (true) 55 | { 56 | wchar_t buf[512]; 57 | UInt32 len = fileStream.ReadLine_w(buf, sizeof(buf) / sizeof(buf[0]), '\n'); 58 | if (len == 0) // End of file 59 | return false; 60 | 61 | // at least $ + wchar_t + \t + wchar_t 62 | if (len < 4 || buf[0] != '$') 63 | continue; 64 | 65 | wchar_t last = buf[len - 1]; 66 | if (last == '\r') 67 | len--; 68 | 69 | // null terminate 70 | buf[len] = 0; 71 | 72 | UInt32 delimIdx = 0; 73 | for (UInt32 i = 0; i < len; i++) 74 | if (buf[i] == '\t') 75 | delimIdx = i; 76 | 77 | // at least $ + wchar_t 78 | if (delimIdx < 2) 79 | continue; 80 | 81 | // replace \t by \0 82 | buf[delimIdx] = 0; 83 | 84 | BSFixedString key(buf); 85 | BSFixedStringW translation(&buf[delimIdx + 1]); 86 | 87 | TranslationTableItem* existing = translator->translations.Find(&key); 88 | if (existing) { 89 | existing->translation = translation; 90 | } else { 91 | TranslationTableItem item(key, translation); 92 | translator->translations.Add(&item); 93 | } 94 | 95 | } 96 | 97 | return true; 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /src/PapyrusMCM.cpp: -------------------------------------------------------------------------------- 1 | #include "PapyrusMCM.h" 2 | #include "Config.h" 3 | #include "SettingStore.h" 4 | #include "ScaleformMCM.h" 5 | 6 | #include "f4se/PapyrusVM.h" 7 | #include "f4se/PapyrusNativeFunctions.h" 8 | 9 | #define MCM_NAME "MCM" 10 | 11 | namespace PapyrusMCM 12 | { 13 | bool IsInstalled(StaticFunctionTag* base) { 14 | return true; 15 | } 16 | 17 | UInt32 GetVersionCode(StaticFunctionTag* base) { 18 | return PLUGIN_VERSION; 19 | } 20 | 21 | void RefreshMenu(StaticFunctionTag* base) { 22 | ScaleformMCM::RefreshMenu(); 23 | } 24 | 25 | SInt32 GetModSettingInt(StaticFunctionTag* base, BSFixedString asModName, BSFixedString asModSetting) { 26 | return SettingStore::GetInstance().GetModSettingInt(asModName.c_str(), asModSetting.c_str()); 27 | } 28 | 29 | bool GetModSettingBool(StaticFunctionTag* base, BSFixedString asModName, BSFixedString asModSetting) { 30 | return SettingStore::GetInstance().GetModSettingBool(asModName.c_str(), asModSetting.c_str()); 31 | } 32 | 33 | float GetModSettingFloat(StaticFunctionTag* base, BSFixedString asModName, BSFixedString asModSetting) { 34 | return SettingStore::GetInstance().GetModSettingFloat(asModName.c_str(), asModSetting.c_str()); 35 | } 36 | 37 | BSFixedString GetModSettingString(StaticFunctionTag* base, BSFixedString asModName, BSFixedString asModSetting) { 38 | BSFixedString str(SettingStore::GetInstance().GetModSettingString(asModName.c_str(), asModSetting.c_str())); 39 | return str; 40 | } 41 | 42 | void SetModSettingInt(StaticFunctionTag* base, BSFixedString asModName, BSFixedString asModSetting, SInt32 abValue) { 43 | SettingStore::GetInstance().SetModSettingInt(asModName.c_str(), asModSetting.c_str(), abValue); 44 | } 45 | 46 | void SetModSettingBool(StaticFunctionTag* base, BSFixedString asModName, BSFixedString asModSetting, bool abValue) { 47 | SettingStore::GetInstance().SetModSettingBool(asModName.c_str(), asModSetting.c_str(), abValue); 48 | } 49 | 50 | void SetModSettingFloat(StaticFunctionTag* base, BSFixedString asModName, BSFixedString asModSetting, float abValue) { 51 | SettingStore::GetInstance().SetModSettingFloat(asModName.c_str(), asModSetting.c_str(), abValue); 52 | } 53 | 54 | void SetModSettingString(StaticFunctionTag* base, BSFixedString asModName, BSFixedString asModSetting, BSFixedString abValue) { 55 | SettingStore::GetInstance().SetModSettingString(asModName.c_str(), asModSetting.c_str(), abValue.c_str()); 56 | } 57 | } 58 | 59 | void PapyrusMCM::RegisterFuncs(VirtualMachine* vm) { 60 | vm->RegisterFunction( 61 | new NativeFunction0("IsInstalled", MCM_NAME, PapyrusMCM::IsInstalled, vm)); 62 | 63 | vm->RegisterFunction( 64 | new NativeFunction0("GetVersionCode", MCM_NAME, PapyrusMCM::GetVersionCode, vm)); 65 | 66 | vm->RegisterFunction( 67 | new NativeFunction0("RefreshMenu", MCM_NAME, PapyrusMCM::RefreshMenu, vm)); 68 | 69 | vm->RegisterFunction( 70 | new NativeFunction2("GetModSettingInt", MCM_NAME, PapyrusMCM::GetModSettingInt, vm)); 71 | 72 | vm->RegisterFunction( 73 | new NativeFunction2("GetModSettingBool", MCM_NAME, PapyrusMCM::GetModSettingBool, vm)); 74 | 75 | vm->RegisterFunction( 76 | new NativeFunction2("GetModSettingFloat", MCM_NAME, PapyrusMCM::GetModSettingFloat, vm)); 77 | 78 | vm->RegisterFunction( 79 | new NativeFunction2("GetModSettingString", MCM_NAME, PapyrusMCM::GetModSettingString, vm)); 80 | 81 | vm->RegisterFunction( 82 | new NativeFunction3("SetModSettingInt", MCM_NAME, PapyrusMCM::SetModSettingInt, vm)); 83 | 84 | vm->RegisterFunction( 85 | new NativeFunction3("SetModSettingBool", MCM_NAME, PapyrusMCM::SetModSettingBool, vm)); 86 | 87 | vm->RegisterFunction( 88 | new NativeFunction3("SetModSettingFloat", MCM_NAME, PapyrusMCM::SetModSettingFloat, vm)); 89 | 90 | vm->RegisterFunction( 91 | new NativeFunction3("SetModSettingString", MCM_NAME, PapyrusMCM::SetModSettingString, vm)); 92 | 93 | vm->SetFunctionFlags(MCM_NAME, "IsInstalled", IFunction::kFunctionFlag_NoWait); 94 | vm->SetFunctionFlags(MCM_NAME, "GetVersionCode", IFunction::kFunctionFlag_NoWait); 95 | } -------------------------------------------------------------------------------- /changelog.txt: -------------------------------------------------------------------------------- 1 | 1.40: 2 | - Public release v1.40 (version code 9) 3 | - Support for game version v1.10.984 4 | 5 | 1.34: 6 | - Public release v1.34 (version code 6) 7 | - Added GetListFromForm Scaleform function 8 | 9 | 1.24: 10 | - Public release v1.24 (version code 4) 11 | - Support for game version v1.10.75 12 | - Fixed: Issue with deactivated mods continuing to satisfy a MCM menu's plugin requirements. 13 | 14 | 1.23: 15 | - Public release v1.23 (version code 4) 16 | - Support for game version v1.10.64 17 | 18 | 1.22: 19 | - Public release v1.22 (version code 4) 20 | - Support for game version v1.10.50 21 | 22 | 1.21: 23 | - Public release v1.21 (version code 4) 24 | - Support for game version v1.10.40 25 | - Started work on game version independence. 26 | 27 | 1.20: 28 | - Public release v1.20 (version code 4) 29 | - No backend changes 30 | 31 | 1.12: 32 | - Public release v1.12 (version code 3) 33 | - Support for game version v1.10.26 34 | 35 | 1.11: 36 | - No backend changes 37 | 38 | 1.10: 39 | - Public release v1.10 (version code 2) 40 | - Automatic fallback to EN strings if translations are not present. 41 | 42 | 1.06: 43 | - Support for ESL plugins 44 | - Updated runtime mismatch error message to be more user-friendly. 45 | - Abstracted Get/SetPropertyValue out of ScaleformMCM and into MCMUtils. 46 | 47 | 1.05: 48 | - Updated runtime mismatch error message to correct swapped version numbers. 49 | - Added mouse and gamepad button conflict detection 50 | - New valueSource type: PropertyValue 51 | - Added MCM Scaleform functions: 52 | - GetPropertyValue(formIdentifier:String, propertyName:String):* 53 | - SetPropertyValue(formIdentifier:String, propertyName:String, newValue:*):Boolean 54 | 55 | 1.0: 56 | - Initial public release. (version code 1) 57 | - Better runtime version checking. 58 | - Enabled mouse hotkeys. 59 | 60 | 0.67: 61 | - Updated MCM Scaleform functions: 62 | - CallQuestFunction can now also call functions on non-Quest forms. 63 | 64 | 0.66: 65 | - Updated MCM Scaleform callback: 66 | - ProcessUserEvent(controlName:String, isDown:Boolean, deviceType:int) 67 | 68 | 0.65: 69 | - Automatically create MCM folder if not present. 70 | - Automatically create MCM\Settings folder if not present. 71 | - Added MCM Scaleform functions: 72 | - OnMCMOpen 73 | - Removed MCM Scaleform functions: 74 | - StartKeyCapture/StopKeyCapture 75 | 76 | 0.64: 77 | - ModSetting defaults are now defined in MCM\Config\ModName\settings.ini 78 | - Keybinds are now serialized to disk when the MCM menu is closed. 79 | - Removed Scaleform function invocation logging. 80 | - Added MCM Scaleform functions: 81 | - OnMCMClose 82 | - Removed MCM Scaleform functions: 83 | - SetKeybindEx 84 | - Updated MCM Scaleform functions: 85 | - GetConfigList(fullPath:Boolean=false, filename:String="config.json") 86 | - Added Papyrus functions: 87 | - RefreshMenu 88 | 89 | 0.63: 90 | - Support for game version 1.10.20 91 | 92 | 0.62: 93 | - Enabled RunConsoleCommand keybind action type. 94 | - Added MCM Scaleform functions: 95 | - DisableMenuInput 96 | 97 | 0.61: 98 | - Added translation support (Interface\Translations\mcm_xx.txt) 99 | 100 | 0.6: 101 | - Added keybind action types: 102 | - CallGlobalFunction 103 | - RunConsoleCommand 104 | - SendEvent (sends OnControlDown/OnControlUp event to target form) 105 | - Support for Control and Alt modifiers for keybinds. 106 | - Added MCM Scaleform functions: 107 | - GetConfigList 108 | - GetMCMVersionCode 109 | - GetMCMVersionString 110 | - Updated MCM Scaleform functions: 111 | - SetKeybind 112 | - Active keybind registrations are now stored in MCM\Settings\Keybinds.json 113 | - Keybind definitions are now stored in MCM\Config\\keybinds.json instead of in the keybind registrations file. 114 | - Support for function parameters for keybind actions. 115 | 116 | 0.5: 117 | - Fixed issue with StopKeyCapture causing CTD if called within ProcessKeyEvent. 118 | - Added keybind/hotkey support 119 | - Added MCM Scaleform functions: 120 | - IsPluginInstalled 121 | - GetAllKeybinds 122 | - GetKeybind 123 | - SetKeybind 124 | - ClearKeybind 125 | - RemapKeybind 126 | 127 | 0.4: 128 | - Fixed a buffer overflow issue that could occur with too many INI settings in a single file. 129 | - Added MCM Scaleform functions: 130 | - StartKeyCapture 131 | - StopKeyCapture 132 | - Added MCM Scaleform callback: 133 | - ProcessKeyEvent(keyCode:int, isDown:Boolean) 134 | 135 | 0.3: 136 | - Added ModSetting support 137 | - Added MCM Scaleform functions: 138 | - Get/SetModSettingInt 139 | - Get/SetModSettingBool 140 | - Get/SetModSettingFloat 141 | - Get/SetModSettingString 142 | 143 | - Added Papyrus functions: 144 | - Get/SetModSettingInt 145 | - Get/SetModSettingBool 146 | - Get/SetModSettingFloat 147 | - Get/SetModSettingString 148 | 149 | 0.2: 150 | - Added MCM Scaleform functions: 151 | - GetGlobalValue 152 | - SetGlobalValue 153 | - CallQuestFunction 154 | - CallGlobalFunction 155 | 156 | 0.1: 157 | - MCM menu injection -------------------------------------------------------------------------------- /src/MCMKeybinds.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include "f4se/PapyrusEvents.h" 4 | #include "f4se/GameTypes.h" 5 | 6 | // Forward-declaration 7 | namespace Json { 8 | class Value; 9 | } 10 | 11 | class Keybind 12 | { 13 | public: 14 | UInt32 keycode; 15 | UInt8 modifiers; 16 | 17 | enum Modifiers { 18 | kModifier_Shift = (1 << 0), 19 | kModifier_Control = (1 << 1), 20 | kModifier_Alt = (1 << 2), 21 | }; 22 | 23 | bool operator<(const Keybind& rhs) const { 24 | if (keycode != rhs.keycode) { 25 | return keycode < rhs.keycode; 26 | } else { 27 | return modifiers < rhs.modifiers; 28 | } 29 | } 30 | 31 | bool operator==(const Keybind& rhs) const { 32 | return (keycode == rhs.keycode && modifiers == rhs.modifiers); 33 | } 34 | }; 35 | 36 | struct ActionParameters 37 | { 38 | enum ParamType { 39 | kType_None, 40 | kType_Int, 41 | kType_Float, 42 | kType_String, 43 | kType_Bool, 44 | }; 45 | 46 | ParamType paramType; 47 | union { 48 | UInt32 iValue; 49 | bool bValue; 50 | float fValue; 51 | BSFixedString sValue; 52 | }; 53 | 54 | ActionParameters() { 55 | paramType = kType_None; 56 | iValue = 0; 57 | } 58 | }; 59 | 60 | class KeybindParameters 61 | { 62 | public: 63 | BSFixedString keybindID; // A unique identifier for this keybind. 64 | BSFixedString keybindDesc; // A short description of the keybind to be shown for conflict resolution. 65 | BSFixedString modName; // The name of the mod that owns this keybind. 66 | UInt8 type; // Action type. 67 | UInt8 flags; // Reserved for future use. 68 | UInt32 targetFormID; // The form ID of the quest form to use 69 | BSFixedString callbackName; // The function to be invoked when this keybind is activated. 70 | BSFixedString scriptName; // The script name to call the function on, for global function invocations. 71 | std::vector actionParams; 72 | 73 | enum Type { 74 | kType_CallFunction, 75 | kType_CallGlobalFunction, 76 | kType_RunConsoleCommand, 77 | kType_SendEvent, 78 | }; 79 | 80 | enum Flags { 81 | kFlag_OnKeyDown = (1 << 0), 82 | kFlag_OnKeyUp = (1 << 1), 83 | kFlag_UserDefined = (1 << 2), 84 | }; 85 | 86 | /*bool operator<(const KeybindParameters& rhs) const { 87 | return (modifiers < rhs.modifiers); 88 | }*/ 89 | }; 90 | 91 | // Used to return keybind information to Papyrus or Scaleform. 92 | // If modifying this structure, also update: 93 | // - GetKeybind(BSFixedString modName, BSFixedString keybindID); 94 | // - GetKeybind(Keybind kb); 95 | // - Scaleform GetKeybind 96 | // - Serialization functions (toJSON and fromJSON) 97 | struct KeybindInfo 98 | { 99 | UInt32 keycode; 100 | UInt8 modifiers; 101 | UInt8 keybindType; // A value in the KeybindType enum. Represents the source of this keybind. 102 | BSFixedString keybindID; // A unique identifier for this keybind. 103 | BSFixedString keybindDesc; // A short description of the keybind to be shown for conflict resolution. 104 | BSFixedString modName; // The name of the mod that owns this keybind. 105 | UInt8 type; // Action type. 106 | UInt8 flags; // Reserved for future use. 107 | BSFixedString callTarget; // Form identifier for CallFunction, Script name for CallGlobalFunction. 108 | BSFixedString callbackName; // The function to be invoked when this keybind is activated. 109 | 110 | enum KeybindType { 111 | kType_Invalid, 112 | kType_MCM, 113 | kType_Game, 114 | kType_F4SE, 115 | }; 116 | }; 117 | 118 | class KeybindManager : public SafeDataHolder> 119 | { 120 | typedef std::map RegMap; 121 | 122 | public: 123 | // Thread-safe 124 | void Register(Keybind key, KeybindParameters & params); 125 | void Clear(void); 126 | 127 | bool RegisterKeybind(Keybind kb, BSFixedString modName, BSFixedString keybindID); // Returns true if the keybind was registered. False if keybind definition could not be located. 128 | 129 | // Serialization 130 | std::string ToJSON(); 131 | bool FromJSON(std::string jsonStr); 132 | void CommitKeybinds(); // Saves registered keybinds to disk if data was changed. (m_keybindsDirty) 133 | bool GetKeybindData(std::string modName, std::string keybindID, KeybindParameters* kp); // Retrieves keybind data from Config\ModName\keybinds.json 134 | void SetActionParams(Json::Value & actionParams, KeybindParameters & kp); 135 | 136 | // Not thread-safe. Explicitly lock before calling these. 137 | KeybindInfo GetKeybind(BSFixedString modName, BSFixedString keybindID); 138 | KeybindInfo GetKeybind(Keybind kb); 139 | std::vector GetAllKeybinds(); 140 | 141 | // Returns true if successfully cleared. False if the keybind did not exist. 142 | bool ClearKeybind(BSFixedString modName, BSFixedString keybindID); 143 | bool ClearKeybind(Keybind kb); 144 | 145 | // Returns true if the keybind was remapped. False if the keybind was not remapped. e.g. if old and new keybinds were the same, or the keybind did not exist. 146 | bool RemapKeybind(BSFixedString modName, BSFixedString keybindID, Keybind newKeybind); 147 | 148 | // Whether or not we should save the keybind information to JSON. 149 | bool m_keybindsDirty = false; 150 | 151 | private: 152 | // Maps a concatentation of modName+keybindID to keybind parameters. 153 | // Data is lazy-loaded. Mod keybind data is loaded from disk into this map when first requested and cached here for future fast lookup. 154 | std::map m_keybindData; 155 | 156 | }; 157 | 158 | extern KeybindManager g_keybindManager; -------------------------------------------------------------------------------- /src/MCMInput.cpp: -------------------------------------------------------------------------------- 1 | #include "MCMInput.h" 2 | #include "ScaleformMCM.h" 3 | 4 | #include "f4se/GameInput.h" 5 | #include "f4se/GameMenus.h" 6 | #include "f4se/InputMap.h" 7 | #include "f4se/PapyrusUtilities.h" 8 | 9 | #include "MCMInput.h" 10 | #include "MCMKeybinds.h" 11 | 12 | #include "Globals.h" 13 | #include "Utils.h" 14 | 15 | void MCMInput::RegisterForInput(bool bRegister) 16 | { 17 | tArray* inputEvents = &((*G::menuControls)->inputEvents); 18 | BSInputEventUser* inputHandler = this; 19 | int idx = inputEvents->GetItemIndex(inputHandler); 20 | if (idx > -1) { 21 | if (!bRegister) { 22 | inputEvents->Remove(idx); 23 | } 24 | } else { 25 | if (bRegister) { 26 | inputEvents->Push(inputHandler); 27 | _MESSAGE("Registered for input events."); 28 | } 29 | } 30 | } 31 | 32 | void PackArgs(VMArray & arguments, KeybindParameters & kp) { 33 | for (int i = 0; i < kp.actionParams.size(); i++) { 34 | VMVariable var; 35 | switch (kp.actionParams[i].paramType) { 36 | case ActionParameters::kType_Int: 37 | var.Set(&kp.actionParams[i].iValue); 38 | break; 39 | case ActionParameters::kType_Bool: 40 | var.Set(&kp.actionParams[i].bValue); 41 | break; 42 | case ActionParameters::kType_Float: 43 | var.Set(&kp.actionParams[i].fValue); 44 | break; 45 | case ActionParameters::kType_String: 46 | var.Set(&kp.actionParams[i].sValue); 47 | break; 48 | } 49 | arguments.Push(&var); 50 | } 51 | } 52 | 53 | void MCMInput::OnButtonEvent(ButtonEvent * inputEvent) 54 | { 55 | UInt32 keyCode; 56 | UInt32 deviceType = inputEvent->deviceType; 57 | UInt32 keyMask = inputEvent->keyMask; 58 | 59 | 60 | if (deviceType == InputEvent::kDeviceType_Mouse) { 61 | // Mouse 62 | if (keyMask < 2 || keyMask > 7) return; // Disallow Mouse1, Mouse2, MouseWheelUp and MouseWheelDown 63 | keyCode = InputMap::kMacro_MouseButtonOffset + keyMask; 64 | } else if (deviceType == InputEvent::kDeviceType_Gamepad) { 65 | // Gamepad 66 | keyCode = InputMap::GamepadMaskToKeycode(keyMask); 67 | } else { 68 | // Keyboard 69 | keyCode = keyMask; 70 | } 71 | 72 | float timer = inputEvent->timer; 73 | bool isDown = inputEvent->isDown == 1.0f && timer == 0.0f; 74 | bool isUp = inputEvent->isDown == 0.0f && timer != 0.0f; 75 | 76 | if (isDown) { 77 | switch (keyCode) { 78 | case 160: 79 | case 161: 80 | case 162: 81 | case 163: 82 | case 164: 83 | case 165: 84 | break; // Shift, Ctrl, Alt modifiers 85 | 86 | default: { 87 | if ((*G::ui)->numPauseGame == 0) { 88 | Keybind kb = {}; 89 | kb.keycode = keyCode; 90 | if (GetAsyncKeyState(VK_SHIFT) & 0x8000) kb.modifiers |= Keybind::kModifier_Shift; 91 | if (GetAsyncKeyState(VK_CONTROL) & 0x8000) kb.modifiers |= Keybind::kModifier_Control; 92 | if (GetAsyncKeyState(VK_MENU) & 0x8000) kb.modifiers |= Keybind::kModifier_Alt; 93 | 94 | g_keybindManager.Lock(); 95 | if (g_keybindManager.m_data.count(kb) > 0) { 96 | KeybindParameters kp = g_keybindManager.m_data[kb]; 97 | g_keybindManager.Release(); 98 | 99 | switch (kp.type) { 100 | case KeybindParameters::kType_CallFunction: 101 | { 102 | TESForm* form = LookupFormByID(kp.targetFormID); 103 | if (form) { 104 | VMArray arguments; 105 | PackArgs(arguments, kp); 106 | CallFunctionNoWait(form, kp.callbackName, arguments); 107 | } else { 108 | _WARNING("Warning: Cannot call a function on a None form."); 109 | } 110 | break; 111 | } 112 | case KeybindParameters::kType_CallGlobalFunction: 113 | { 114 | if (strlen(kp.callbackName.c_str()) > 0) { 115 | VirtualMachine * vm = (*G::gameVM)->m_virtualMachine; 116 | VMArray arguments; 117 | PackArgs(arguments, kp); 118 | VMValue args; 119 | PackValue(&args, &arguments, vm); 120 | CallGlobalFunctionNoWait_Internal(vm, 0, 0, &kp.scriptName, &kp.callbackName, &args); 121 | } 122 | break; 123 | } 124 | case KeybindParameters::kType_RunConsoleCommand: 125 | { 126 | if (strlen(kp.callbackName.c_str()) > 0) { 127 | MCMUtils::ExecuteCommand(kp.callbackName.c_str()); 128 | } 129 | break; 130 | } 131 | case KeybindParameters::kType_SendEvent: 132 | { 133 | TESForm* form = LookupFormByID(kp.targetFormID); 134 | if (form) { 135 | UInt64 handle = PapyrusVM::GetHandleFromObject(form, TESForm::kTypeID); 136 | SendPapyrusEvent1(handle, "ScriptObject", "OnControlDown", kp.keybindID); 137 | } else { 138 | _WARNING("Warning: Cannot send an event to a None form."); 139 | } 140 | break; 141 | } 142 | default: 143 | _WARNING("Warning: Cannot execute a keybind with unknown action type."); 144 | break; 145 | } 146 | 147 | } else { 148 | g_keybindManager.Release(); 149 | } 150 | } 151 | 152 | break; 153 | } 154 | } 155 | 156 | 157 | } else if (isUp) { 158 | switch (keyCode) { 159 | case 160: 160 | case 161: 161 | case 162: 162 | case 163: 163 | case 164: 164 | case 165: 165 | break; // Shift, Ctrl, Alt modifiers 166 | 167 | default: { 168 | if ((*G::ui)->numPauseGame == 0) { 169 | Keybind kb = {}; 170 | kb.keycode = keyCode; 171 | kb.modifiers = 0; 172 | 173 | g_keybindManager.Lock(); 174 | if (g_keybindManager.m_data.count(kb) > 0) { 175 | KeybindParameters kp = g_keybindManager.m_data[kb]; 176 | g_keybindManager.Release(); 177 | 178 | switch (kp.type) { 179 | case KeybindParameters::kType_SendEvent: 180 | { 181 | TESForm* form = LookupFormByID(kp.targetFormID); 182 | if (form) { 183 | UInt64 handle = PapyrusVM::GetHandleFromObject(form, TESForm::kTypeID); 184 | SendPapyrusEvent2(handle, "ScriptObject", "OnControlUp", kp.keybindID, timer); 185 | } else { 186 | _WARNING("Warning: Cannot send an event to a None form."); 187 | } 188 | break; 189 | } 190 | } 191 | } else { 192 | g_keybindManager.Release(); 193 | } 194 | } 195 | 196 | break; 197 | } 198 | } 199 | } 200 | } 201 | 202 | -------------------------------------------------------------------------------- /src/Utils.cpp: -------------------------------------------------------------------------------- 1 | #include "Utils.h" 2 | #include "Config.h" 3 | 4 | #include 5 | 6 | #include "f4se_common/SafeWrite.h" 7 | #include "f4se/GameData.h" 8 | #include "f4se/PapyrusVM.h" 9 | #include "f4se/PapyrusArgs.h" 10 | 11 | #include "rva/RVA.h" 12 | #include "Globals.h" 13 | 14 | //--------------------- 15 | // Function Signatures 16 | //--------------------- 17 | 18 | // VM -> Unk23 19 | typedef bool (*_SetPropertyValue)(VirtualMachine* vm, VMIdentifier** identifier, const char* propertyName, VMValue* newValue, UInt64* unk4); 20 | const int Idx_SetPropertyValue = 0x23; 21 | 22 | // VM -> Unk25 23 | typedef bool (*_GetPropertyValueByIndex)(VirtualMachine* vm, VMIdentifier** identifier, int idx, VMValue* outValue); 24 | const int Idx_GetPropertyValueByIndex = 0x25; 25 | 26 | template 27 | T GetVirtualFunction(void* baseObject, int vtblIndex) { 28 | uintptr_t* vtbl = reinterpret_cast(baseObject)[0]; 29 | return reinterpret_cast(vtbl[vtblIndex]); 30 | } 31 | 32 | template 33 | T GetOffset(const void* baseObject, int offset) { 34 | return *reinterpret_cast((uintptr_t)baseObject + offset); 35 | } 36 | 37 | //--------------------- 38 | // Addresses [3] 39 | //--------------------- 40 | 41 | typedef void (*_ExecuteCommand)(const char* str); 42 | RVA <_ExecuteCommand> ExecuteCommand_Internal("40 55 53 56 57 41 56 41 57 48 8D AC 24 ? ? ? ? 48 81 EC 88 08 00 00"); 43 | 44 | RVA ProcessUserEvent_Check("40 55 56 41 56 48 8D 6C 24 ? 48 81 EC ? ? ? ? 48 8B 02 4C 8B F1 48 8B CA 0F 29 B4 24 ? ? ? ?", 0x8C); 45 | 46 | typedef void* (*_GetPropertyInfo)(VMObjectTypeInfo* objectTypeInfo, void* outInfo, BSFixedString* propertyName, bool unk4); // unk4 = 1 47 | RVA <_GetPropertyInfo> GetPropertyInfo_Internal("48 89 43 10 89 43 18 48 89 43 20 48 89 43 28 C7 43 ? ? ? ? ? 89 43 34 E8 ? ? ? ?", -0x34); 48 | 49 | //--------------------- 50 | // Functions 51 | //--------------------- 52 | 53 | TESForm * MCMUtils::GetFormFromIdentifier(const std::string & identifier) 54 | { 55 | auto delimiter = identifier.find('|'); 56 | if (delimiter != std::string::npos) { 57 | std::string modName = identifier.substr(0, delimiter); 58 | std::string modForm = identifier.substr(delimiter + 1); 59 | 60 | const ModInfo* mod = (*G::dataHandler)->LookupModByName(modName.c_str()); 61 | if (mod && mod->modIndex != 0xFF) { 62 | UInt32 formID = std::stoul(modForm, nullptr, 16) & 0xFFFFFF; 63 | UInt32 flags = GetOffset(mod, 0x334); 64 | if (flags & (1 << 9)) { 65 | // ESL 66 | formID &= 0xFFF; 67 | formID |= 0xFE << 24; 68 | formID |= GetOffset(mod, 0x372) << 12; // ESL load order 69 | } else { 70 | formID |= (mod->modIndex) << 24; 71 | } 72 | return LookupFormByID(formID); 73 | } 74 | } 75 | return nullptr; 76 | } 77 | 78 | std::string MCMUtils::GetIdentifierFromForm(const TESForm & form) 79 | { 80 | return GetIdentifierFromFormID(form.formID); 81 | } 82 | 83 | // Note: This function does not yet support ESLs. 84 | // This result of this function is not currently being used in the MCM. 85 | std::string MCMUtils::GetIdentifierFromFormID(UInt32 formID) 86 | { 87 | UInt8 modIndex = formID >> 24; 88 | if (modIndex >= 0xFE) return ""; // ESL or user-created 89 | 90 | ModInfo* mod = (*G::dataHandler)->modList.loadedMods[modIndex]; 91 | 92 | char formIDStr[9]; 93 | 94 | if (mod) { 95 | std::string output = mod->name; 96 | output += "|"; 97 | snprintf(formIDStr, sizeof(formIDStr), "%X", formID & 0xFFFFFF); 98 | output += formIDStr; 99 | return output; 100 | } 101 | 102 | return ""; 103 | } 104 | 105 | void MCMUtils::ExecuteCommand(const char * cmd) 106 | { 107 | ExecuteCommand_Internal(cmd); 108 | } 109 | 110 | void MCMUtils::DisableProcessUserEvent(bool disable) 111 | { 112 | if (disable) { 113 | unsigned char data[] = { 0x90, 0xE9 }; // NOP, JMP 114 | SafeWriteBuf(ProcessUserEvent_Check.GetUIntPtr(), &data, sizeof(data)); 115 | } else { 116 | unsigned char data[] = { 0x0F, 0x84 }; // JZ (original) 117 | SafeWriteBuf(ProcessUserEvent_Check.GetUIntPtr(), &data, sizeof(data)); 118 | } 119 | } 120 | 121 | void MCMUtils::GetPropertyInfo(VMObjectTypeInfo * objectTypeInfo, PropertyInfo * outInfo, BSFixedString * propertyName) 122 | { 123 | GetPropertyInfo_Internal(objectTypeInfo, outInfo, propertyName, 1); 124 | } 125 | 126 | bool MCMUtils::GetPropertyValue(const char * formIdentifier, const char * scriptName, const char * propertyName, VMValue * valueOut) 127 | { 128 | TESForm* targetForm = MCMUtils::GetFormFromIdentifier(formIdentifier); 129 | if (!targetForm) { 130 | _WARNING("Warning: Cannot retrieve property value %s from a None form. (%s)", propertyName, formIdentifier); 131 | return false; 132 | } 133 | 134 | VirtualMachine* vm = (*G::gameVM)->m_virtualMachine; 135 | VMScript script(targetForm, scriptName); 136 | 137 | if (!script.m_identifier) { 138 | _WARNING("Warning: Cannot retrieve a property value %s from a form with no scripts attached. (%s)", propertyName, formIdentifier); 139 | return false; 140 | } 141 | 142 | // Find the property 143 | PropertyInfo pInfo = {}; 144 | pInfo.index = -1; 145 | GetPropertyInfo(script.m_identifier->m_typeInfo, &pInfo, &BSFixedString(propertyName)); 146 | 147 | if (pInfo.index != -1) { 148 | vm->GetPropertyValueByIndex(&script.m_identifier, pInfo.index, valueOut); 149 | return true; 150 | } else { 151 | _WARNING("Warning: Property %s does not exist on script %s", propertyName, script.m_identifier->m_typeInfo->m_typeName.c_str()); 152 | return false; 153 | } 154 | } 155 | 156 | bool MCMUtils::SetPropertyValue(const char * formIdentifier, const char * scriptName, const char * propertyName, VMValue * valueIn) 157 | { 158 | TESForm* targetForm = GetFormFromIdentifier(formIdentifier); 159 | if (!targetForm) { 160 | _WARNING("Warning: Cannot set property %s on a None form. (%s)", propertyName, formIdentifier); 161 | return false; 162 | } 163 | 164 | VirtualMachine* vm = (*G::gameVM)->m_virtualMachine; 165 | VMScript script(targetForm, scriptName); 166 | 167 | if (!script.m_identifier) { 168 | _WARNING("Warning: Cannot set a property value %s on a form with no scripts attached. (%s)", propertyName, formIdentifier); 169 | return false; 170 | } 171 | 172 | UInt64 unk4 = 0; 173 | vm->SetPropertyValue(&script.m_identifier, propertyName, valueIn, &unk4); 174 | 175 | return true; 176 | } 177 | 178 | #include "f4se\GameRTTI.h" 179 | 180 | BSFixedString MCMUtils::GetDescription(TESForm * thisForm) 181 | { 182 | if (!thisForm) 183 | return BSFixedString(); 184 | 185 | TESDescription * pDescription = DYNAMIC_CAST(thisForm, TESForm, TESDescription); 186 | if (pDescription) { 187 | BSString str; 188 | CALL_MEMBER_FN(pDescription, Get)(&str, nullptr); 189 | return str.Get(); 190 | } 191 | 192 | return BSFixedString(); 193 | } -------------------------------------------------------------------------------- /src/MCM.cpp: -------------------------------------------------------------------------------- 1 | #include "f4se/PluginAPI.h" 2 | #include "f4se/GameAPI.h" 3 | #include 4 | 5 | #include "f4se/PluginManager.h" 6 | 7 | #include "f4se_common/f4se_version.h" 8 | 9 | #include "f4se/ScaleformValue.h" 10 | #include "f4se/ScaleformMovie.h" 11 | #include "f4se/ScaleformCallbacks.h" 12 | 13 | // Translation 14 | #include "f4se/ScaleformLoader.h" 15 | #include "f4se/Translation.h" 16 | 17 | #include "f4se/PapyrusVM.h" 18 | #include "f4se/PapyrusNativeFunctions.h" 19 | 20 | #include "Config.h" 21 | #include "rva/RVA.h" 22 | 23 | #include "Globals.h" 24 | #include "PapyrusMCM.h" 25 | #include "ScaleformMCM.h" 26 | #include "SettingStore.h" 27 | #include "MCMInput.h" 28 | #include "MCMSerialization.h" 29 | #include "MCMTranslator.h" 30 | 31 | IDebugLog gLog; 32 | PluginHandle g_pluginHandle = kPluginHandle_Invalid; 33 | 34 | F4SEScaleformInterface *g_scaleform = NULL; 35 | F4SEPapyrusInterface *g_papyrus = NULL; 36 | F4SEMessagingInterface *g_messaging = NULL; 37 | F4SESerializationInterface *g_serialization = NULL; 38 | 39 | //------------------------- 40 | // Event Handlers 41 | //------------------------- 42 | 43 | bool RegisterPapyrus(VirtualMachine *vm) { 44 | PapyrusMCM::RegisterFuncs(vm); 45 | _MESSAGE("Registered Papyrus native functions."); 46 | 47 | return true; 48 | } 49 | 50 | void OnF4SEMessage(F4SEMessagingInterface::Message* msg) { 51 | switch (msg->type) { 52 | case F4SEMessagingInterface::kMessage_GameLoaded: 53 | MCMInput::GetInstance().RegisterForInput(true); 54 | 55 | // Inject translations 56 | BSScaleformTranslator* translator = (BSScaleformTranslator*)(*G::scaleformManager)->stateBag->GetStateAddRef(GFxState::kInterface_Translator); 57 | if (translator) { 58 | MCMTranslator::LoadTranslations(translator); 59 | } 60 | break; 61 | } 62 | } 63 | 64 | extern "C" 65 | { 66 | 67 | __declspec(dllexport) F4SEPluginVersionData F4SEPlugin_Version = 68 | { 69 | F4SEPluginVersionData::kVersion, 70 | 71 | PLUGIN_VERSION, 72 | PLUGIN_NAME_SHORT, 73 | "The F4MCM Authors", 74 | 75 | 0, 76 | F4SEPluginVersionData::kStructureIndependence_1_10_980Layout, 77 | { SUPPORTED_RUNTIME_VERSION, 0 }, 78 | 79 | 0, 80 | }; 81 | 82 | bool F4SEPlugin_Query(const F4SEInterface * f4se, PluginInfo * info) 83 | { 84 | gLog.OpenRelative(CSIDL_MYDOCUMENTS, "\\My Games\\Fallout4\\F4SE\\MCM.log"); 85 | 86 | _MESSAGE("MCM v%s", PLUGIN_VERSION_STRING); 87 | _MESSAGE("MCM query"); 88 | 89 | // populate info structure 90 | info->infoVersion = PluginInfo::kInfoVersion; 91 | info->name = PLUGIN_NAME_SHORT; 92 | info->version = PLUGIN_VERSION; 93 | 94 | // store plugin handle so we can identify ourselves later 95 | g_pluginHandle = f4se->GetPluginHandle(); 96 | 97 | // Check game version 98 | if (f4se->runtimeVersion != SUPPORTED_RUNTIME_VERSION) { 99 | char str[512]; 100 | sprintf_s(str, sizeof(str), "Your game version: v%d.%d.%d.%d\nExpected version: v%d.%d.%d.%d\n%s will be disabled.", 101 | GET_EXE_VERSION_MAJOR(f4se->runtimeVersion), 102 | GET_EXE_VERSION_MINOR(f4se->runtimeVersion), 103 | GET_EXE_VERSION_BUILD(f4se->runtimeVersion), 104 | GET_EXE_VERSION_SUB(f4se->runtimeVersion), 105 | GET_EXE_VERSION_MAJOR(SUPPORTED_RUNTIME_VERSION), 106 | GET_EXE_VERSION_MINOR(SUPPORTED_RUNTIME_VERSION), 107 | GET_EXE_VERSION_BUILD(SUPPORTED_RUNTIME_VERSION), 108 | GET_EXE_VERSION_SUB(SUPPORTED_RUNTIME_VERSION), 109 | PLUGIN_NAME_LONG 110 | ); 111 | 112 | MessageBox(NULL, str, PLUGIN_NAME_LONG, MB_OK | MB_ICONEXCLAMATION); 113 | return false; 114 | } 115 | 116 | // Get the scaleform interface 117 | g_scaleform = (F4SEScaleformInterface *)f4se->QueryInterface(kInterface_Scaleform); 118 | if(!g_scaleform) { 119 | _MESSAGE("couldn't get scaleform interface"); 120 | return false; 121 | } 122 | 123 | // Get the papyrus interface 124 | g_papyrus = (F4SEPapyrusInterface *)f4se->QueryInterface(kInterface_Papyrus); 125 | if (!g_papyrus) { 126 | _MESSAGE("couldn't get papyrus interface"); 127 | return false; 128 | } 129 | 130 | // Get the messaging interface 131 | g_messaging = (F4SEMessagingInterface *)f4se->QueryInterface(kInterface_Messaging); 132 | if (!g_messaging) { 133 | _MESSAGE("couldn't get messaging interface"); 134 | return false; 135 | } 136 | 137 | // Get the serialization interface 138 | g_serialization = (F4SESerializationInterface *)f4se->QueryInterface(kInterface_Serialization); 139 | if (!g_serialization) { 140 | _MESSAGE("couldn't get serialization interface"); 141 | return false; 142 | } 143 | 144 | return true; 145 | } 146 | 147 | bool F4SEPlugin_Load(const F4SEInterface *f4se) 148 | { 149 | gLog.OpenRelative(CSIDL_MYDOCUMENTS, "\\My Games\\Fallout4\\F4SE\\MCM.log"); 150 | 151 | _MESSAGE("MCM v%s", PLUGIN_VERSION_STRING); 152 | _MESSAGE("MCM load"); 153 | 154 | // Store plugin handle 155 | g_pluginHandle = f4se->GetPluginHandle(); 156 | 157 | // Get interfaces 158 | // Get the scaleform interface 159 | g_scaleform = (F4SEScaleformInterface*)f4se->QueryInterface(kInterface_Scaleform); 160 | if (!g_scaleform) { 161 | _MESSAGE("couldn't get scaleform interface"); 162 | return false; 163 | } 164 | 165 | // Get the papyrus interface 166 | g_papyrus = (F4SEPapyrusInterface*)f4se->QueryInterface(kInterface_Papyrus); 167 | if (!g_papyrus) { 168 | _MESSAGE("couldn't get papyrus interface"); 169 | return false; 170 | } 171 | 172 | // Get the messaging interface 173 | g_messaging = (F4SEMessagingInterface*)f4se->QueryInterface(kInterface_Messaging); 174 | if (!g_messaging) { 175 | _MESSAGE("couldn't get messaging interface"); 176 | return false; 177 | } 178 | 179 | // Get the serialization interface 180 | g_serialization = (F4SESerializationInterface*)f4se->QueryInterface(kInterface_Serialization); 181 | if (!g_serialization) { 182 | _MESSAGE("couldn't get serialization interface"); 183 | return false; 184 | } 185 | 186 | // Initialize globals and addresses 187 | G::Init(); 188 | RVAManager::UpdateAddresses(f4se->runtimeVersion); 189 | 190 | g_scaleform->Register("f4mcm", ScaleformMCM::RegisterScaleform); 191 | g_papyrus->Register(RegisterPapyrus); 192 | g_messaging->RegisterListener(g_pluginHandle, "F4SE", OnF4SEMessage); 193 | 194 | g_serialization->SetUniqueID(g_pluginHandle, 'MCM'); 195 | g_serialization->SetRevertCallback(g_pluginHandle, MCMSerialization::RevertCallback); 196 | g_serialization->SetLoadCallback(g_pluginHandle, MCMSerialization::LoadCallback); 197 | g_serialization->SetSaveCallback(g_pluginHandle, MCMSerialization::SaveCallback); 198 | 199 | // Create Data/MCM if it doesn't already exist. 200 | if (GetFileAttributes("Data\\MCM") == INVALID_FILE_ATTRIBUTES) 201 | CreateDirectory("Data\\MCM", NULL); 202 | 203 | SettingStore::GetInstance().ReadSettings(); 204 | 205 | return true; 206 | } 207 | 208 | }; 209 | -------------------------------------------------------------------------------- /src/f4mcm.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | x64 7 | 8 | 9 | Release 10 | x64 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | {d4c128a1-73dc-4941-a453-ce55af239ba8} 30 | true 31 | 32 | 33 | {a236f69d-8ff9-4491-ac5f-45bf49448bbe} 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | {B90CE001-A134-45D2-9B64-C70FF2607C6E} 54 | Win32Proj 55 | f4mcm 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 10.0.22621.0 65 | 66 | 67 | 68 | DynamicLibrary 69 | true 70 | v142 71 | MultiByte 72 | 73 | 74 | DynamicLibrary 75 | false 76 | v142 77 | true 78 | MultiByte 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | true 92 | mcm 93 | 94 | 95 | false 96 | mcm 97 | 98 | 99 | 100 | 101 | 102 | Level3 103 | Disabled 104 | WIN32;_DEBUG;_WINDOWS;_USRDLL;F4SE_EXPORTS;%(PreprocessorDefinitions) 105 | $(SolutionDir);$(SolutionDir)\..;%(AdditionalIncludeDirectories) 106 | common/IPrefix.h 107 | MultiThreadedDebug 108 | 109 | 110 | Windows 111 | true 112 | exports.def 113 | 114 | 115 | 116 | 117 | copy /b /y "$(TargetPath)" "$(F4SE_PLUGINS)\$(TargetFileName)" 118 | 119 | 120 | Installing DLL... 121 | 122 | 123 | 124 | 125 | Level3 126 | 127 | 128 | MaxSpeed 129 | true 130 | true 131 | WIN32;NDEBUG;_WINDOWS;_USRDLL;F4SE_EXPORTS;RUNTIME;%(PreprocessorDefinitions) 132 | $(SolutionDir);$(SolutionDir)\..;%(AdditionalIncludeDirectories) 133 | common/IPrefix.h 134 | MultiThreaded 135 | 136 | 137 | Windows 138 | true 139 | true 140 | true 141 | exports.def 142 | 143 | 144 | copy /b /y "$(TargetPath)" "$(F4SE_PLUGINS)\$(TargetFileName)" 145 | 146 | 147 | Installing DLL... 148 | 149 | 150 | 151 | 152 | 153 | -------------------------------------------------------------------------------- /src/SettingStore.cpp: -------------------------------------------------------------------------------- 1 | #include "SettingStore.h" 2 | 3 | #include 4 | 5 | // reg2k 6 | Setting::~Setting() { 7 | delete name; 8 | if (GetType() == kType_String) { 9 | delete data.s; 10 | } 11 | } 12 | 13 | SettingStore::SettingStore() 14 | { 15 | _MESSAGE("ModSettingStore initializing."); 16 | } 17 | 18 | SInt32 SettingStore::GetModSettingInt(std::string modName, std::string settingName) 19 | { 20 | Setting* ms = GetModSetting(modName, settingName); 21 | if (ms) { 22 | return ms->data.s32; 23 | } 24 | return -1; 25 | } 26 | 27 | void SettingStore::SetModSettingInt(std::string modName, std::string settingName, SInt32 newValue) 28 | { 29 | Setting* ms = GetModSetting(modName, settingName); 30 | if (ms && ms->data.s32 != newValue) { 31 | ms->data.s32 = newValue; 32 | CommitModSetting(modName, ms); 33 | } 34 | } 35 | 36 | bool SettingStore::GetModSettingBool(std::string modName, std::string settingName) 37 | { 38 | Setting* ms = GetModSetting(modName, settingName); 39 | if (ms) { 40 | return (ms->data.u8) > 0; 41 | } 42 | return false; 43 | } 44 | 45 | void SettingStore::SetModSettingBool(std::string modName, std::string settingName, bool newValue) 46 | { 47 | Setting* ms = GetModSetting(modName, settingName); 48 | if (ms && ms->data.u8 != (newValue ? 1 : 0)) { 49 | ms->data.u8 = newValue; 50 | CommitModSetting(modName, ms); 51 | } 52 | } 53 | 54 | float SettingStore::GetModSettingFloat(std::string modName, std::string settingName) 55 | { 56 | Setting* ms = GetModSetting(modName, settingName); 57 | if (ms) { 58 | return ms->data.f32; 59 | } 60 | return -1; 61 | } 62 | 63 | void SettingStore::SetModSettingFloat(std::string modName, std::string settingName, float newValue) 64 | { 65 | Setting* ms = GetModSetting(modName, settingName); 66 | if (ms && ms->data.f32 != newValue) { 67 | ms->data.f32 = newValue; 68 | CommitModSetting(modName, ms); 69 | } 70 | } 71 | 72 | char* SettingStore::GetModSettingString(std::string modName, std::string settingName) 73 | { 74 | Setting* ms = GetModSetting(modName, settingName); 75 | if (ms) { 76 | return ms->data.s; 77 | } 78 | return nullptr; 79 | } 80 | 81 | void SettingStore::SetModSettingString(std::string modName, std::string settingName, const char* newValue) 82 | { 83 | Setting* ms = GetModSetting(modName, settingName); 84 | if (ms && strcmp(ms->data.s, newValue) != 0) { 85 | if (ms->data.s) delete ms->data.s; 86 | ms->data.s = new char[strlen(newValue)+1]; 87 | strcpy_s(ms->data.s, strlen(newValue) + 1, newValue); 88 | CommitModSetting(modName, ms); 89 | } 90 | } 91 | 92 | // Read ModSettings from filesystem. 93 | void SettingStore::ReadSettings() { 94 | // - Load defaults from MCM\Config\Mod\defaults.ini 95 | // - Load user settings from MCM\Settings\Mod.ini 96 | // - For each file: 97 | // - Get all section names with GetPrivateProfileSectionNames 98 | // - Get all key/value pairs in each section with GetPrivateProfileSection 99 | // - Store them in m_settingStore 100 | 101 | LARGE_INTEGER countStart, countEnd, frequency; 102 | QueryPerformanceCounter(&countStart); 103 | QueryPerformanceFrequency(&frequency); 104 | 105 | LoadDefaults(); 106 | LoadUserSettings(); 107 | 108 | QueryPerformanceCounter(&countEnd); 109 | long long int elapsed = (countEnd.QuadPart - countStart.QuadPart) / (frequency.QuadPart / 1000); 110 | _MESSAGE("Registered %d mod settings in %llu ms.", m_settingStore.size(), elapsed); 111 | } 112 | 113 | //---------------------- 114 | // Private Functions 115 | //---------------------- 116 | 117 | void SettingStore::LoadDefaults() { 118 | // Find all defaults.ini files. 119 | HANDLE hFind; 120 | WIN32_FIND_DATA data; 121 | 122 | hFind = FindFirstFile("Data\\MCM\\Config\\*", &data); 123 | if (hFind != INVALID_HANDLE_VALUE) { 124 | do { 125 | if (!(data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) continue; 126 | if (!strcmp(data.cFileName, ".") || !strcmp(data.cFileName, "..")) continue; 127 | 128 | char fullPath[MAX_PATH]; 129 | snprintf(fullPath, MAX_PATH, "%s%s%s", "Data\\MCM\\Config\\", data.cFileName, "\\settings.ini"); 130 | 131 | if (GetFileAttributes(fullPath) == INVALID_FILE_ATTRIBUTES) continue; 132 | 133 | //_MESSAGE("name %s path %s", data.cFileName, fullPath); 134 | 135 | // Read into settings 136 | ReadINI(data.cFileName, fullPath); 137 | 138 | } while (FindNextFile(hFind, &data)); 139 | FindClose(hFind); 140 | } 141 | } 142 | 143 | void SettingStore::LoadUserSettings() { 144 | char* modSettingsDirectory = "Data\\MCM\\Settings\\*.ini"; 145 | 146 | HANDLE hFind; 147 | WIN32_FIND_DATA data; 148 | std::vector modSettingFiles; 149 | 150 | hFind = FindFirstFile(modSettingsDirectory, &data); 151 | if (hFind != INVALID_HANDLE_VALUE) { 152 | do { 153 | modSettingFiles.push_back(data); 154 | } while (FindNextFile(hFind, &data)); 155 | FindClose(hFind); 156 | } 157 | 158 | _MESSAGE("Number of mod setting files: %d", modSettingFiles.size()); 159 | 160 | for (int i = 0; i < modSettingFiles.size(); i++) { 161 | std::string iniLocation = "./Data/MCM/Settings/"; 162 | iniLocation += modSettingFiles[i].cFileName; 163 | 164 | // Extract mod name 165 | std::string modName(modSettingFiles[i].cFileName); 166 | modName = modName.substr(0, modName.find_last_of('.')); 167 | 168 | ReadINI(modName, iniLocation); 169 | } 170 | } 171 | 172 | bool SettingStore::ReadINI(std::string modName, std::string iniLocation) { 173 | 174 | //_MESSAGE("Loading mod settings for %s.", modName.c_str()); 175 | 176 | // Extract all sections 177 | std::vector sections; 178 | LPTSTR lpszReturnBuffer = new TCHAR[1024]; 179 | DWORD sizeWritten = GetPrivateProfileSectionNames(lpszReturnBuffer, 1024, iniLocation.c_str()); 180 | if (sizeWritten == (1024 - 2)) { 181 | _WARNING("Warning: Too many sections. Settings will not be read."); 182 | delete lpszReturnBuffer; 183 | return false; 184 | } 185 | 186 | for (LPTSTR p = lpszReturnBuffer; *p; p++) { 187 | std::string sectionName(p); 188 | sections.push_back(sectionName); 189 | p += strlen(p); 190 | ASSERT(*p == 0); 191 | } 192 | 193 | delete lpszReturnBuffer; 194 | 195 | //_MESSAGE("Number of sections: %d", sections.size()); 196 | 197 | for (int j = 0; j < sections.size(); j++) { 198 | // Extract all keys within section 199 | int len = 1024; 200 | LPTSTR lpReturnedString = new TCHAR[len]; 201 | DWORD sizeWritten = GetPrivateProfileSection(sections[j].c_str(), lpReturnedString, len, iniLocation.c_str()); 202 | while (sizeWritten == (len - 2)) { 203 | // Buffer too small to contain all entries; expand buffer and try again. 204 | delete lpReturnedString; 205 | len <<= 1; 206 | lpReturnedString = new TCHAR[len]; 207 | sizeWritten = GetPrivateProfileSection(sections[j].c_str(), lpReturnedString, len, iniLocation.c_str()); 208 | _MESSAGE("Expanded buffer to %d bytes.", len); 209 | } 210 | 211 | for (LPTSTR p = lpReturnedString; *p; p++) { 212 | std::string valuePair(p); 213 | 214 | //_MESSAGE("%s", p); 215 | auto delimiter = valuePair.find_first_of('='); 216 | std::string settingName = valuePair.substr(0, delimiter) + ":" + sections[j]; 217 | /*if (sections[j] != "Main" && sections[j] != modName) { 218 | settingName += ":" + sections[j]; 219 | }*/ 220 | std::string settingValue = valuePair.substr(delimiter + 1); 221 | RegisterModSetting(modName, settingName, settingValue); 222 | 223 | p += strlen(p); 224 | ASSERT(*p == 0); 225 | } 226 | 227 | delete lpReturnedString; 228 | } 229 | 230 | return true; 231 | } 232 | 233 | Setting * SettingStore::GetModSetting(std::string modName, std::string settingName) 234 | { 235 | auto itr = m_settingStore.find(modName + ":" + settingName); 236 | if (itr != m_settingStore.end()) { 237 | return itr->second; 238 | } 239 | return nullptr; 240 | } 241 | 242 | void SettingStore::RegisterModSetting(std::string modName, std::string settingName, std::string settingValue) 243 | { 244 | Setting* ms = new Setting; 245 | 246 | char* nameCopy = new char[settingName.size()+1]; 247 | std::copy(settingName.begin(), settingName.end(), nameCopy); 248 | nameCopy[settingName.size()] = '\0'; 249 | ms->name = nameCopy; 250 | 251 | switch (ms->GetType()) { 252 | case Setting::kType_Bool: 253 | ms->data.u8 = settingValue != "0"; 254 | break; 255 | 256 | case Setting::kType_Float: 257 | ms->data.f32 = std::stof(settingValue); 258 | break; 259 | 260 | case Setting::kType_Integer: 261 | ms->data.s32 = std::stoi(settingValue); 262 | break; 263 | 264 | case Setting::kType_String: { 265 | ms->data.s = new char[settingValue.size() + 1]; 266 | std::copy(settingValue.begin(), settingValue.end(), ms->data.s); 267 | ms->data.s[settingValue.size()] = '\0'; 268 | break; 269 | } 270 | 271 | default: 272 | _WARNING("WARNING: ModSetting %s from mod %s has an unknown type and cannot be registered.", settingName.c_str(), modName.c_str()); 273 | delete ms; 274 | return; 275 | } 276 | 277 | m_settingStore[modName + ":" + settingName] = ms; 278 | } 279 | 280 | void SettingStore::CommitModSetting(std::string modName, Setting* modSetting) 281 | { 282 | std::string modSettingName(modSetting->name); 283 | auto delimiter = modSettingName.find_first_of(':'); 284 | if (delimiter != std::string::npos) { 285 | std::string settingName = modSettingName.substr(0, delimiter); 286 | std::string sectionName = modSettingName.substr(delimiter+1); 287 | std::string value; 288 | 289 | switch (modSetting->GetType()) { 290 | case Setting::kType_Bool: 291 | value = std::to_string(modSetting->data.u8 & 1); 292 | break; 293 | case Setting::kType_Integer: 294 | value = std::to_string(modSetting->data.s32); 295 | break; 296 | case Setting::kType_Float: 297 | value = std::to_string(modSetting->data.f32); 298 | break; 299 | case Setting::kType_String: 300 | value = modSetting->data.s; 301 | break; 302 | default: 303 | _WARNING("WARNING: ModSetting %s from mod %s has an unknown type and cannot be saved.", settingName.c_str(), modName.c_str()); 304 | return; 305 | } 306 | 307 | std::string iniPath = "./Data/MCM/Settings/" + modName + ".ini"; 308 | if (GetFileAttributes("Data\\MCM\\Settings") == INVALID_FILE_ATTRIBUTES) 309 | CreateDirectory("Data\\MCM\\Settings", NULL); 310 | WritePrivateProfileString(sectionName.c_str(), settingName.c_str(), value.c_str(), iniPath.c_str()); 311 | } else { 312 | _WARNING("Error: Section could not be resolved."); 313 | } 314 | 315 | 316 | } 317 | -------------------------------------------------------------------------------- /src/json/json-forwards.h: -------------------------------------------------------------------------------- 1 | /// Json-cpp amalgated forward header (http://jsoncpp.sourceforge.net/). 2 | /// It is intended to be used with #include "json/json-forwards.h" 3 | /// This header provides forward declaration for all JsonCpp types. 4 | 5 | // ////////////////////////////////////////////////////////////////////// 6 | // Beginning of content of file: LICENSE 7 | // ////////////////////////////////////////////////////////////////////// 8 | 9 | /* 10 | The JsonCpp library's source code, including accompanying documentation, 11 | tests and demonstration applications, are licensed under the following 12 | conditions... 13 | 14 | The JsonCpp Authors explicitly disclaim copyright in all 15 | jurisdictions which recognize such a disclaimer. In such jurisdictions, 16 | this software is released into the Public Domain. 17 | 18 | In jurisdictions which do not recognize Public Domain property (e.g. Germany as of 19 | 2010), this software is Copyright (c) 2007-2010 by The JsonCpp Authors, and is 20 | released under the terms of the MIT License (see below). 21 | 22 | In jurisdictions which recognize Public Domain property, the user of this 23 | software may choose to accept it either as 1) Public Domain, 2) under the 24 | conditions of the MIT License (see below), or 3) under the terms of dual 25 | Public Domain/MIT License conditions described here, as they choose. 26 | 27 | The MIT License is about as close to Public Domain as a license can get, and is 28 | described in clear, concise terms at: 29 | 30 | http://en.wikipedia.org/wiki/MIT_License 31 | 32 | The full text of the MIT License follows: 33 | 34 | ======================================================================== 35 | Copyright (c) 2007-2010 The JsonCpp Authors 36 | 37 | Permission is hereby granted, free of charge, to any person 38 | obtaining a copy of this software and associated documentation 39 | files (the "Software"), to deal in the Software without 40 | restriction, including without limitation the rights to use, copy, 41 | modify, merge, publish, distribute, sublicense, and/or sell copies 42 | of the Software, and to permit persons to whom the Software is 43 | furnished to do so, subject to the following conditions: 44 | 45 | The above copyright notice and this permission notice shall be 46 | included in all copies or substantial portions of the Software. 47 | 48 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 49 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 50 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 51 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 52 | BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 53 | ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 54 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 55 | SOFTWARE. 56 | ======================================================================== 57 | (END LICENSE TEXT) 58 | 59 | The MIT license is compatible with both the GPL and commercial 60 | software, affording one all of the rights of Public Domain with the 61 | minor nuisance of being required to keep the above copyright notice 62 | and license text in the source code. Note also that by accepting the 63 | Public Domain "license" you can re-license your copy using whatever 64 | license you like. 65 | 66 | */ 67 | 68 | // ////////////////////////////////////////////////////////////////////// 69 | // End of content of file: LICENSE 70 | // ////////////////////////////////////////////////////////////////////// 71 | 72 | 73 | 74 | 75 | 76 | #ifndef JSON_FORWARD_AMALGATED_H_INCLUDED 77 | # define JSON_FORWARD_AMALGATED_H_INCLUDED 78 | /// If defined, indicates that the source file is amalgated 79 | /// to prevent private header inclusion. 80 | #define JSON_IS_AMALGAMATION 81 | 82 | // ////////////////////////////////////////////////////////////////////// 83 | // Beginning of content of file: include/json/config.h 84 | // ////////////////////////////////////////////////////////////////////// 85 | 86 | // Copyright 2007-2010 Baptiste Lepilleur 87 | // Distributed under MIT license, or public domain if desired and 88 | // recognized in your jurisdiction. 89 | // See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE 90 | 91 | #ifndef JSON_CONFIG_H_INCLUDED 92 | #define JSON_CONFIG_H_INCLUDED 93 | #include 94 | #include //typedef String 95 | #include //typedef int64_t, uint64_t 96 | 97 | /// If defined, indicates that json library is embedded in CppTL library. 98 | //# define JSON_IN_CPPTL 1 99 | 100 | /// If defined, indicates that json may leverage CppTL library 101 | //# define JSON_USE_CPPTL 1 102 | /// If defined, indicates that cpptl vector based map should be used instead of 103 | /// std::map 104 | /// as Value container. 105 | //# define JSON_USE_CPPTL_SMALLMAP 1 106 | 107 | // If non-zero, the library uses exceptions to report bad input instead of C 108 | // assertion macros. The default is to use exceptions. 109 | #ifndef JSON_USE_EXCEPTION 110 | #define JSON_USE_EXCEPTION 1 111 | #endif 112 | 113 | /// If defined, indicates that the source file is amalgated 114 | /// to prevent private header inclusion. 115 | /// Remarks: it is automatically defined in the generated amalgated header. 116 | // #define JSON_IS_AMALGAMATION 117 | 118 | #ifdef JSON_IN_CPPTL 119 | #include 120 | #ifndef JSON_USE_CPPTL 121 | #define JSON_USE_CPPTL 1 122 | #endif 123 | #endif 124 | 125 | #ifdef JSON_IN_CPPTL 126 | #define JSON_API CPPTL_API 127 | #elif defined(JSON_DLL_BUILD) 128 | #if defined(_MSC_VER) || defined(__MINGW32__) 129 | #define JSON_API __declspec(dllexport) 130 | #define JSONCPP_DISABLE_DLL_INTERFACE_WARNING 131 | #endif // if defined(_MSC_VER) 132 | #elif defined(JSON_DLL) 133 | #if defined(_MSC_VER) || defined(__MINGW32__) 134 | #define JSON_API __declspec(dllimport) 135 | #define JSONCPP_DISABLE_DLL_INTERFACE_WARNING 136 | #endif // if defined(_MSC_VER) 137 | #endif // ifdef JSON_IN_CPPTL 138 | #if !defined(JSON_API) 139 | #define JSON_API 140 | #endif 141 | 142 | // If JSON_NO_INT64 is defined, then Json only support C++ "int" type for 143 | // integer 144 | // Storages, and 64 bits integer support is disabled. 145 | // #define JSON_NO_INT64 1 146 | 147 | #if defined(_MSC_VER) // MSVC 148 | # if _MSC_VER <= 1200 // MSVC 6 149 | // Microsoft Visual Studio 6 only support conversion from __int64 to double 150 | // (no conversion from unsigned __int64). 151 | # define JSON_USE_INT64_DOUBLE_CONVERSION 1 152 | // Disable warning 4786 for VS6 caused by STL (identifier was truncated to '255' 153 | // characters in the debug information) 154 | // All projects I've ever seen with VS6 were using this globally (not bothering 155 | // with pragma push/pop). 156 | # pragma warning(disable : 4786) 157 | # endif // MSVC 6 158 | 159 | # if _MSC_VER >= 1500 // MSVC 2008 160 | /// Indicates that the following function is deprecated. 161 | # define JSONCPP_DEPRECATED(message) __declspec(deprecated(message)) 162 | # endif 163 | 164 | #endif // defined(_MSC_VER) 165 | 166 | // In c++11 the override keyword allows you to explicity define that a function 167 | // is intended to override the base-class version. This makes the code more 168 | // managable and fixes a set of common hard-to-find bugs. 169 | #if __cplusplus >= 201103L 170 | # define JSONCPP_OVERRIDE override 171 | # define JSONCPP_NOEXCEPT noexcept 172 | #elif defined(_MSC_VER) && _MSC_VER > 1600 && _MSC_VER < 1900 173 | # define JSONCPP_OVERRIDE override 174 | # define JSONCPP_NOEXCEPT throw() 175 | #elif defined(_MSC_VER) && _MSC_VER >= 1900 176 | # define JSONCPP_OVERRIDE override 177 | # define JSONCPP_NOEXCEPT noexcept 178 | #else 179 | # define JSONCPP_OVERRIDE 180 | # define JSONCPP_NOEXCEPT throw() 181 | #endif 182 | 183 | #ifndef JSON_HAS_RVALUE_REFERENCES 184 | 185 | #if defined(_MSC_VER) && _MSC_VER >= 1600 // MSVC >= 2010 186 | #define JSON_HAS_RVALUE_REFERENCES 1 187 | #endif // MSVC >= 2010 188 | 189 | #ifdef __clang__ 190 | #if __has_feature(cxx_rvalue_references) 191 | #define JSON_HAS_RVALUE_REFERENCES 1 192 | #endif // has_feature 193 | 194 | #elif defined __GNUC__ // not clang (gcc comes later since clang emulates gcc) 195 | #if defined(__GXX_EXPERIMENTAL_CXX0X__) || (__cplusplus >= 201103L) 196 | #define JSON_HAS_RVALUE_REFERENCES 1 197 | #endif // GXX_EXPERIMENTAL 198 | 199 | #endif // __clang__ || __GNUC__ 200 | 201 | #endif // not defined JSON_HAS_RVALUE_REFERENCES 202 | 203 | #ifndef JSON_HAS_RVALUE_REFERENCES 204 | #define JSON_HAS_RVALUE_REFERENCES 0 205 | #endif 206 | 207 | #ifdef __clang__ 208 | #elif defined __GNUC__ // not clang (gcc comes later since clang emulates gcc) 209 | # if (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5)) 210 | # define JSONCPP_DEPRECATED(message) __attribute__ ((deprecated(message))) 211 | # elif (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1)) 212 | # define JSONCPP_DEPRECATED(message) __attribute__((__deprecated__)) 213 | # endif // GNUC version 214 | #endif // __clang__ || __GNUC__ 215 | 216 | #if !defined(JSONCPP_DEPRECATED) 217 | #define JSONCPP_DEPRECATED(message) 218 | #endif // if !defined(JSONCPP_DEPRECATED) 219 | 220 | #if __GNUC__ >= 6 221 | # define JSON_USE_INT64_DOUBLE_CONVERSION 1 222 | #endif 223 | 224 | #if !defined(JSON_IS_AMALGAMATION) 225 | 226 | # include "version.h" 227 | 228 | # if JSONCPP_USING_SECURE_MEMORY 229 | # include "allocator.h" //typedef Allocator 230 | # endif 231 | 232 | #endif // if !defined(JSON_IS_AMALGAMATION) 233 | 234 | namespace Json { 235 | typedef int Int; 236 | typedef unsigned int UInt; 237 | #if defined(JSON_NO_INT64) 238 | typedef int LargestInt; 239 | typedef unsigned int LargestUInt; 240 | #undef JSON_HAS_INT64 241 | #else // if defined(JSON_NO_INT64) 242 | // For Microsoft Visual use specific types as long long is not supported 243 | #if defined(_MSC_VER) // Microsoft Visual Studio 244 | typedef __int64 Int64; 245 | typedef unsigned __int64 UInt64; 246 | #else // if defined(_MSC_VER) // Other platforms, use long long 247 | typedef int64_t Int64; 248 | typedef uint64_t UInt64; 249 | #endif // if defined(_MSC_VER) 250 | typedef Int64 LargestInt; 251 | typedef UInt64 LargestUInt; 252 | #define JSON_HAS_INT64 253 | #endif // if defined(JSON_NO_INT64) 254 | #if JSONCPP_USING_SECURE_MEMORY 255 | #define JSONCPP_STRING std::basic_string, Json::SecureAllocator > 256 | #define JSONCPP_OSTRINGSTREAM std::basic_ostringstream, Json::SecureAllocator > 257 | #define JSONCPP_OSTREAM std::basic_ostream> 258 | #define JSONCPP_ISTRINGSTREAM std::basic_istringstream, Json::SecureAllocator > 259 | #define JSONCPP_ISTREAM std::istream 260 | #else 261 | #define JSONCPP_STRING std::string 262 | #define JSONCPP_OSTRINGSTREAM std::ostringstream 263 | #define JSONCPP_OSTREAM std::ostream 264 | #define JSONCPP_ISTRINGSTREAM std::istringstream 265 | #define JSONCPP_ISTREAM std::istream 266 | #endif // if JSONCPP_USING_SECURE_MEMORY 267 | } // end namespace Json 268 | 269 | #endif // JSON_CONFIG_H_INCLUDED 270 | 271 | // ////////////////////////////////////////////////////////////////////// 272 | // End of content of file: include/json/config.h 273 | // ////////////////////////////////////////////////////////////////////// 274 | 275 | 276 | 277 | 278 | 279 | 280 | // ////////////////////////////////////////////////////////////////////// 281 | // Beginning of content of file: include/json/forwards.h 282 | // ////////////////////////////////////////////////////////////////////// 283 | 284 | // Copyright 2007-2010 Baptiste Lepilleur 285 | // Distributed under MIT license, or public domain if desired and 286 | // recognized in your jurisdiction. 287 | // See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE 288 | 289 | #ifndef JSON_FORWARDS_H_INCLUDED 290 | #define JSON_FORWARDS_H_INCLUDED 291 | 292 | #if !defined(JSON_IS_AMALGAMATION) 293 | #include "config.h" 294 | #endif // if !defined(JSON_IS_AMALGAMATION) 295 | 296 | namespace Json { 297 | 298 | // writer.h 299 | class FastWriter; 300 | class StyledWriter; 301 | 302 | // reader.h 303 | class Reader; 304 | 305 | // features.h 306 | class Features; 307 | 308 | // value.h 309 | typedef unsigned int ArrayIndex; 310 | class StaticString; 311 | class Path; 312 | class PathArgument; 313 | class Value; 314 | class ValueIteratorBase; 315 | class ValueIterator; 316 | class ValueConstIterator; 317 | 318 | } // namespace Json 319 | 320 | #endif // JSON_FORWARDS_H_INCLUDED 321 | 322 | // ////////////////////////////////////////////////////////////////////// 323 | // End of content of file: include/json/forwards.h 324 | // ////////////////////////////////////////////////////////////////////// 325 | 326 | 327 | 328 | 329 | 330 | #endif //ifndef JSON_FORWARD_AMALGATED_H_INCLUDED 331 | -------------------------------------------------------------------------------- /src/MCMKeybinds.cpp: -------------------------------------------------------------------------------- 1 | #include "MCMKeybinds.h" 2 | #include 3 | #include 4 | 5 | #include "Globals.h" 6 | #include "Utils.h" 7 | 8 | #include "json/json.h" 9 | 10 | #include "f4se/GameInput.h" 11 | #include "f4se/InputMap.h" 12 | 13 | KeybindManager g_keybindManager; 14 | 15 | void KeybindManager::Register(Keybind key, KeybindParameters & params) 16 | { 17 | Lock(); 18 | m_data[key] = params; 19 | Release(); 20 | } 21 | 22 | void KeybindManager::Clear(void) 23 | { 24 | Lock(); 25 | m_data.clear(); 26 | Release(); 27 | } 28 | 29 | //------------------------------ 30 | // Serialization 31 | //------------------------------ 32 | 33 | std::string KeybindManager::ToJSON() 34 | { 35 | Lock(); 36 | std::vector keybinds = GetAllKeybinds(); 37 | Release(); 38 | 39 | Json::Value json; 40 | json["version"] = 1; 41 | 42 | for (int i = 0; i < keybinds.size(); i++) { 43 | Json::Value keybind; 44 | keybind["keycode"] = (int) keybinds[i].keycode; 45 | keybind["modifiers"] = (int) keybinds[i].modifiers; 46 | keybind["modName"] = keybinds[i].modName.c_str(); 47 | keybind["id"] = keybinds[i].keybindID.c_str(); 48 | 49 | json["keybinds"][i] = keybind; 50 | } 51 | 52 | Json::FastWriter fast; 53 | std::string jsonStr = fast.write(json); 54 | return jsonStr; 55 | } 56 | 57 | bool KeybindManager::FromJSON(std::string jsonStr) 58 | { 59 | try { 60 | Json::Value json, keybinds; 61 | Json::Reader reader; 62 | reader.parse(jsonStr, json); 63 | 64 | if (json["version"].asInt() < 1) return false; 65 | 66 | keybinds = json["keybinds"]; 67 | if (!keybinds.isArray()) return false; 68 | 69 | for (int i = 0; i < keybinds.size(); i++) { 70 | const Json::Value& keybind = keybinds[i]; 71 | 72 | Keybind kb = {}; 73 | KeybindParameters kp = {}; 74 | 75 | kb.keycode = keybind["keycode"].asInt(); 76 | kb.modifiers = keybind["modifiers"].asInt(); 77 | 78 | std::string modName = keybind["modName"].asString(); 79 | std::string keybindID = keybind["id"].asString(); 80 | 81 | if (GetKeybindData(modName, keybindID, &kp)) { 82 | Lock(); 83 | m_data[kb] = kp; 84 | Release(); 85 | } else { 86 | _MESSAGE("Warning: Failed to get keybind data for %s with keybind ID %s", modName.c_str(), keybindID.c_str()); 87 | continue; 88 | } 89 | } 90 | 91 | return true; 92 | } catch (...) { 93 | _WARNING("Warning: Keybind storage deserialization failure. No keybinds will be loaded."); 94 | return false; 95 | } 96 | } 97 | 98 | void KeybindManager::CommitKeybinds() 99 | { 100 | if (m_keybindsDirty) { 101 | // Save keybinds only if the data has changed. 102 | LARGE_INTEGER countStart, countEnd, frequency; 103 | QueryPerformanceCounter(&countStart); 104 | QueryPerformanceFrequency(&frequency); 105 | 106 | Lock(); 107 | try { 108 | _MESSAGE("Serializing keybinds..."); 109 | if (GetFileAttributes("Data\\MCM\\Settings") == INVALID_FILE_ATTRIBUTES) 110 | CreateDirectory("Data\\MCM\\Settings", NULL); 111 | std::ofstream file("Data\\MCM\\Settings\\Keybinds.json"); 112 | std::string jsonStr = ToJSON(); 113 | file << jsonStr; 114 | file.close(); 115 | m_keybindsDirty = false; 116 | } catch (...) { 117 | _MESSAGE("Warning: An error occurred when serializing keybinds."); 118 | } 119 | Release(); 120 | 121 | QueryPerformanceCounter(&countEnd); 122 | _MESSAGE("Elapsed: %llu ms.", (countEnd.QuadPart - countStart.QuadPart) / (frequency.QuadPart / 1000)); 123 | } 124 | } 125 | 126 | bool KeybindManager::GetKeybindData(std::string modName, std::string keybindID, KeybindParameters * kp) 127 | { 128 | // Check if we've already cached the data. 129 | std::string keyName = modName + keybindID; 130 | std::transform(keyName.begin(), keyName.end(), keyName.begin(), ::tolower); 131 | if (m_keybindData.count(keyName) == 0) { 132 | try { 133 | // Not in the cache. Load from disk. 134 | auto idx = modName.find_last_of('.'); // Strip trailing .esp / .esm 135 | std::string filePath = "Data\\MCM\\Config\\" + modName.substr(0, idx) + "\\keybinds.json"; 136 | _MESSAGE("Loading keybind definitions for %s", modName.c_str()); 137 | std::ifstream file(filePath); 138 | if (file.is_open()) { 139 | Json::Value json, keybinds; 140 | Json::Reader reader; 141 | reader.parse(file, json); 142 | 143 | keybinds = json["keybinds"]; 144 | if (!keybinds.isArray()) return false; 145 | 146 | for (int i = 0; i < keybinds.size(); i++) { 147 | const Json::Value& keybind = keybinds[i]; 148 | 149 | KeybindParameters kp = {}; 150 | kp.modName = json["modName"].asCString(); 151 | kp.keybindID = keybind["id"].asCString(); 152 | kp.keybindDesc = keybind["desc"].asCString(); 153 | //kp.flags = keybind["flags"].asInt(); 154 | kp.type = -1; 155 | 156 | std::string typeStr = keybind["action"]["type"].asString(); 157 | if (typeStr == "CallFunction") kp.type = KeybindParameters::kType_CallFunction; 158 | else if (typeStr == "CallGlobalFunction") kp.type = KeybindParameters::kType_CallGlobalFunction; 159 | else if (typeStr == "RunConsoleCommand") kp.type = KeybindParameters::kType_RunConsoleCommand; 160 | else if (typeStr == "SendEvent") kp.type = KeybindParameters::kType_SendEvent; 161 | 162 | switch (kp.type) { 163 | case KeybindParameters::kType_CallFunction: 164 | { 165 | TESForm* form = MCMUtils::GetFormFromIdentifier(keybind["action"]["form"].asString()); 166 | if (form) { 167 | kp.targetFormID = form->formID; 168 | kp.callbackName = keybind["action"]["function"].asCString(); 169 | 170 | Json::Value actionParams = keybind["action"]["params"]; 171 | if (!actionParams.isNull()) SetActionParams(actionParams, kp); 172 | } else { 173 | // Invalid form. 174 | continue; 175 | } 176 | break; 177 | } 178 | case KeybindParameters::kType_CallGlobalFunction: 179 | { 180 | kp.scriptName = keybind["action"]["script"].asCString(); 181 | kp.callbackName = keybind["action"]["function"].asCString(); 182 | 183 | Json::Value actionParams = keybind["action"]["params"]; 184 | if (!actionParams.isNull()) SetActionParams(actionParams, kp); 185 | 186 | break; 187 | } 188 | case KeybindParameters::kType_RunConsoleCommand: 189 | { 190 | kp.callbackName = keybind["action"]["command"].asCString(); 191 | break; 192 | } 193 | case KeybindParameters::kType_SendEvent: 194 | { 195 | TESForm* form = MCMUtils::GetFormFromIdentifier(keybind["action"]["form"].asString()); 196 | if (form) { 197 | kp.targetFormID = form->formID; 198 | } else { 199 | // Invalid form. 200 | continue; 201 | } 202 | break; 203 | } 204 | default: 205 | { 206 | _WARNING("Warning: Cannot deserialize invalid keybind action type %s.", typeStr.c_str()); 207 | continue; 208 | break; 209 | } 210 | } 211 | 212 | std::string keyName = json["modName"].asString() + keybind["id"].asString(); 213 | std::transform(keyName.begin(), keyName.end(), keyName.begin(), ::tolower); 214 | m_keybindData[keyName] = kp; 215 | } 216 | } else { 217 | // Keybinds file doesn't exist. 218 | return false; 219 | } 220 | } catch (...) { 221 | _WARNING("Warning: Failed to parse malformed keybind definition file for mod %s.", modName.c_str()); 222 | return false; 223 | } 224 | } 225 | 226 | if (m_keybindData.count(keyName) > 0) { 227 | *kp = m_keybindData[keyName]; 228 | return true; 229 | } else { 230 | // The keybind doesn't exist anymore. 231 | return false; 232 | } 233 | } 234 | 235 | void KeybindManager::SetActionParams(Json::Value & actionParams, KeybindParameters & kp) 236 | { 237 | if (actionParams.isArray()) { 238 | for (int i = 0; i < actionParams.size(); i++) { 239 | ActionParameters ap; 240 | switch (actionParams[i].type()) { 241 | case Json::intValue: 242 | ap.paramType = ActionParameters::kType_Int; 243 | ap.iValue = actionParams[i].asInt(); 244 | break; 245 | case Json::booleanValue: 246 | ap.paramType = ActionParameters::kType_Bool; 247 | ap.bValue = actionParams[i].asBool(); 248 | break; 249 | case Json::realValue: 250 | ap.paramType = ActionParameters::kType_Float; 251 | ap.fValue = actionParams[i].asFloat(); 252 | break; 253 | case Json::stringValue: 254 | ap.paramType = ActionParameters::kType_String; 255 | ap.sValue = actionParams[i].asCString(); 256 | break; 257 | default: 258 | // Unknown value type 259 | _MESSAGE("Cannot register unknown parameter value type: %s", actionParams[i].type()); 260 | break; 261 | } 262 | if (ap.paramType != ActionParameters::kType_None) { 263 | kp.actionParams.push_back(ap); 264 | } 265 | } 266 | } 267 | } 268 | 269 | KeybindInfo KeybindManager::GetKeybind(BSFixedString modName, BSFixedString keybindID) 270 | { 271 | for (RegMap::iterator iter = m_data.begin(); iter != m_data.end(); iter++) { 272 | if (iter->second.modName == modName && iter->second.keybindID == keybindID) { 273 | return GetKeybind(iter->first); 274 | } 275 | } 276 | KeybindInfo ki = {}; 277 | return ki; 278 | } 279 | 280 | KeybindInfo KeybindManager::GetKeybind(Keybind kb) 281 | { 282 | KeybindInfo ki = {}; 283 | ki.keybindType = KeybindInfo::kType_Invalid; 284 | ki.keycode = kb.keycode; 285 | ki.modifiers = kb.modifiers; 286 | 287 | if (m_data.find(kb) != m_data.end()) { 288 | ki.keybindType = KeybindInfo::kType_MCM; 289 | ki.keybindID = m_data[kb].keybindID; 290 | ki.keybindDesc = m_data[kb].keybindDesc; 291 | ki.modName = m_data[kb].modName; 292 | ki.type = m_data[kb].type; 293 | ki.flags = m_data[kb].flags; 294 | ki.callbackName = m_data[kb].callbackName; 295 | 296 | switch (ki.type) { 297 | case KeybindParameters::kType_CallFunction: 298 | case KeybindParameters::kType_SendEvent: 299 | { 300 | ki.callTarget = MCMUtils::GetIdentifierFromFormID(m_data[kb].targetFormID).c_str(); 301 | break; 302 | } 303 | case KeybindParameters::kType_CallGlobalFunction: 304 | { 305 | ki.callTarget = m_data[kb].scriptName; 306 | break; 307 | } 308 | case KeybindParameters::kType_RunConsoleCommand: 309 | { 310 | ki.callTarget = m_data[kb].callbackName; 311 | break; 312 | } 313 | } 314 | } else { 315 | // Not in registered keybinds. Check game keybinds. 316 | if (kb.keycode > 0) { 317 | BSFixedString controlName(""); 318 | 319 | if (kb.keycode < InputMap::kMacro_MouseButtonOffset) { 320 | // KB 321 | controlName = (*G::inputMgr)->GetMappedControl(kb.keycode, InputEvent::kDeviceType_Keyboard, InputManager::kContext_Gameplay); 322 | 323 | } else if (kb.keycode < InputMap::kMacro_GamepadOffset) { 324 | // Mouse 325 | controlName = (*G::inputMgr)->GetMappedControl(kb.keycode - InputMap::kMacro_MouseButtonOffset, InputEvent::kDeviceType_Mouse, InputManager::kContext_Gameplay); 326 | 327 | } else if (kb.keycode < InputMap::kMaxMacros) { 328 | // Gamepad 329 | controlName = (*G::inputMgr)->GetMappedControl(InputMap::GamepadKeycodeToMask(kb.keycode), InputEvent::kDeviceType_Gamepad, InputManager::kContext_Gameplay); 330 | } 331 | 332 | if (strcmp("", controlName.c_str()) != 0) { 333 | ki.keybindType = KeybindInfo::kType_Game; 334 | ki.keybindID = ""; 335 | ki.keybindDesc = controlName; 336 | ki.modName = "Fallout4.esm"; 337 | ki.type = 0; 338 | ki.flags = 0; 339 | ki.callTarget = ""; 340 | ki.callbackName = ""; 341 | } 342 | } 343 | } 344 | 345 | return ki; 346 | } 347 | 348 | std::vector KeybindManager::GetAllKeybinds() 349 | { 350 | std::vector keybinds; 351 | for (RegMap::iterator iter = m_data.begin(); iter != m_data.end(); iter++) { 352 | KeybindInfo ki = GetKeybind(iter->first); 353 | keybinds.push_back(ki); 354 | } 355 | return keybinds; 356 | } 357 | 358 | bool KeybindManager::RegisterKeybind(Keybind kb, BSFixedString modName, BSFixedString keybindID) 359 | { 360 | KeybindParameters kp = {}; 361 | if (GetKeybindData(modName.c_str(), keybindID.c_str(), &kp)) { 362 | Lock(); 363 | m_data[kb] = kp; 364 | Release(); 365 | m_keybindsDirty = true; 366 | return true; 367 | } else { 368 | return false; 369 | } 370 | } 371 | 372 | bool KeybindManager::ClearKeybind(BSFixedString modName, BSFixedString keybindID) 373 | { 374 | for (RegMap::iterator iter = m_data.begin(); iter != m_data.end(); iter++) { 375 | if (iter->second.modName == modName && iter->second.keybindID == keybindID) { 376 | m_data.erase(iter); 377 | m_keybindsDirty = true; 378 | return true; 379 | } 380 | } 381 | return false; 382 | } 383 | 384 | bool KeybindManager::ClearKeybind(Keybind kb) 385 | { 386 | auto iter = m_data.find(kb); 387 | if (iter != m_data.end()) { 388 | m_data.erase(iter); 389 | m_keybindsDirty = true; 390 | return true; 391 | } else { 392 | return false; 393 | } 394 | } 395 | 396 | bool KeybindManager::RemapKeybind(BSFixedString modName, BSFixedString keybindID, Keybind newKeybind) 397 | { 398 | for (RegMap::iterator iter = m_data.begin(); iter != m_data.end(); iter++) { 399 | if (iter->second.modName == modName && iter->second.keybindID == keybindID) { 400 | Keybind oldKeybind = iter->first; 401 | if (oldKeybind == newKeybind) return false; 402 | m_data[newKeybind] = iter->second; 403 | m_data.erase(iter); 404 | m_keybindsDirty = true; 405 | return true; 406 | } 407 | } 408 | return false; 409 | } 410 | -------------------------------------------------------------------------------- /src/ScaleformMCM.cpp: -------------------------------------------------------------------------------- 1 | #include "ScaleformMCM.h" 2 | 3 | #include "f4se/ScaleformValue.h" 4 | #include "f4se/ScaleformMovie.h" 5 | #include "f4se/ScaleformCallbacks.h" 6 | #include "f4se/PapyrusScaleformAdapter.h" 7 | 8 | #include "f4se/PapyrusEvents.h" 9 | #include "f4se/PapyrusUtilities.h" 10 | 11 | #include "f4se/GameData.h" 12 | #include "f4se/GameRTTI.h" 13 | #include "f4se/GameMenus.h" 14 | #include "f4se/GameInput.h" 15 | #include "f4se/InputMap.h" 16 | 17 | #include "Globals.h" 18 | #include "Config.h" 19 | #include "Utils.h" 20 | #include "SettingStore.h" 21 | #include "MCMKeybinds.h" 22 | 23 | namespace ScaleformMCM { 24 | 25 | // function GetMCMVersionString():String; 26 | class GetMCMVersionString : public GFxFunctionHandler { 27 | public: 28 | virtual void Invoke(Args* args) { 29 | args->result->SetString(PLUGIN_VERSION_STRING); 30 | } 31 | }; 32 | 33 | // function GetMCMVersionCode():int; 34 | class GetMCMVersionCode : public GFxFunctionHandler { 35 | public: 36 | virtual void Invoke(Args* args) { 37 | args->result->SetInt(PLUGIN_VERSION); 38 | } 39 | }; 40 | 41 | // function GetConfigList(fullPath:Boolean=false, filename:String="config.json"):Array; 42 | // Returns: ["Mod1", "Mod2", "Mod3"] (fullPath = false), or ["Data\MCM\Config\Mod1\config.json", ...] (fullPath = true) 43 | class GetConfigList : public GFxFunctionHandler { 44 | public: 45 | virtual void Invoke(Args* args) { 46 | bool wantFullPath = (args->numArgs > 0 && args->args[0].GetType() == GFxValue::kType_Bool) ? args->args[0].GetBool() : false; 47 | const char* filename = (args->numArgs > 1 && args->args[1].GetType() == GFxValue::kType_String) ? args->args[1].GetString() : "config.json"; 48 | 49 | args->movie->movieRoot->CreateArray(args->result); 50 | 51 | HANDLE hFind; 52 | WIN32_FIND_DATA data; 53 | 54 | hFind = FindFirstFile("Data\\MCM\\Config\\*", &data); 55 | if (hFind != INVALID_HANDLE_VALUE) { 56 | do { 57 | if (!(data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) continue; 58 | if (!strcmp(data.cFileName, ".") || !strcmp(data.cFileName, "..")) continue; 59 | 60 | char fullPath[MAX_PATH]; 61 | snprintf(fullPath, MAX_PATH, "%s%s%s%s", "Data\\MCM\\Config\\", data.cFileName, "\\", filename); 62 | 63 | if (GetFileAttributes(fullPath) == INVALID_FILE_ATTRIBUTES) continue; 64 | 65 | GFxValue filePath; 66 | filePath.SetString(wantFullPath ? fullPath : data.cFileName); 67 | args->result->PushBack(&filePath); 68 | } while (FindNextFile(hFind, &data)); 69 | FindClose(hFind); 70 | } 71 | } 72 | }; 73 | 74 | // function OnMCMOpen(); 75 | class OnMCMOpen : public GFxFunctionHandler { 76 | public: 77 | virtual void Invoke(Args* args) { 78 | // Start key handler 79 | RegisterForInput(true); 80 | } 81 | }; 82 | 83 | // function OnMCMClose(); 84 | class OnMCMClose : public GFxFunctionHandler { 85 | public: 86 | virtual void Invoke(Args* args) { 87 | // Save modified keybinds. 88 | g_keybindManager.CommitKeybinds(); 89 | RegisterForInput(false); 90 | } 91 | }; 92 | 93 | // function DisableMenuInput(disable:Boolean); 94 | class DisableMenuInput : public GFxFunctionHandler { 95 | public: 96 | virtual void Invoke(Args* args) { 97 | if (args->numArgs < 1) return; 98 | if (args->args[0].GetType() != GFxValue::kType_Bool) return; 99 | bool disable = args->args[0].GetBool(); 100 | 101 | MCMUtils::DisableProcessUserEvent(disable); 102 | } 103 | }; 104 | 105 | // function GetGlobalValue(formIdentifier:String):Number 106 | class GetGlobalValue : public GFxFunctionHandler { 107 | public: 108 | virtual void Invoke(Args* args) { 109 | args->result->SetNumber(-1); 110 | 111 | if (args->numArgs != 1) return; 112 | if (args->args[0].GetType() != GFxValue::kType_String) return; 113 | 114 | TESForm* targetForm = MCMUtils::GetFormFromIdentifier(args->args[0].GetString()); 115 | TESGlobal* global = nullptr; 116 | global = DYNAMIC_CAST(targetForm, TESForm, TESGlobal); 117 | 118 | if (global) { 119 | args->result->SetNumber(global->value); 120 | } 121 | } 122 | }; 123 | 124 | // function SetGlobalValue(formIdentifier:String, newValue:Number):Boolean 125 | class SetGlobalValue : public GFxFunctionHandler { 126 | public: 127 | virtual void Invoke(Args* args) { 128 | args->result->SetBool(false); 129 | 130 | if (args->numArgs != 2) return; 131 | if (args->args[0].GetType() != GFxValue::kType_String) return; 132 | if (args->args[1].GetType() != GFxValue::kType_Number) return; 133 | 134 | TESForm* targetForm = MCMUtils::GetFormFromIdentifier(args->args[0].GetString()); 135 | TESGlobal* global = nullptr; 136 | global = DYNAMIC_CAST(targetForm, TESForm, TESGlobal); 137 | 138 | if (global) { 139 | global->value = args->args[1].GetNumber(); 140 | args->result->SetBool(true); 141 | } 142 | } 143 | }; 144 | 145 | // function GetPropertyValue(formIdentifier:String, propertyName:String):* 146 | // Returns null if the property doesn't exist. 147 | class GetPropertyValue : public GFxFunctionHandler { 148 | public: 149 | virtual void Invoke(Args* args) { 150 | args->result->SetNull(); 151 | 152 | if (args->numArgs < 2) return; 153 | if (args->args[0].GetType() != GFxValue::kType_String) return; 154 | if (args->args[1].GetType() != GFxValue::kType_String) return; 155 | 156 | const char* formIdentifier = args->args[0].GetString(); 157 | const char* propertyName = args->args[1].GetString(); 158 | 159 | VMValue valueOut; 160 | bool getOK = MCMUtils::GetPropertyValue(formIdentifier, nullptr, propertyName, &valueOut); 161 | if (getOK) 162 | PlatformAdapter::ConvertPapyrusValue(args->result, &valueOut, args->movie->movieRoot); 163 | } 164 | }; 165 | 166 | // function SetPropertyValue(formIdentifier:String, propertyName:String, newValue:*):Boolean 167 | class SetPropertyValue : public GFxFunctionHandler { 168 | public: 169 | virtual void Invoke(Args* args) { 170 | args->result->SetBool(false); 171 | 172 | if (args->numArgs < 3) return; 173 | if (args->args[0].GetType() != GFxValue::kType_String) return; 174 | if (args->args[1].GetType() != GFxValue::kType_String) return; 175 | 176 | const char* formIdentifier = args->args[0].GetString(); 177 | const char* propertyName = args->args[1].GetString(); 178 | GFxValue* newValue = &args->args[2]; 179 | 180 | VirtualMachine* vm = (*G::gameVM)->m_virtualMachine; 181 | 182 | VMValue newVMValue; 183 | PlatformAdapter::ConvertScaleformValue(&newVMValue, newValue, vm); 184 | bool setOK = MCMUtils::SetPropertyValue(formIdentifier, nullptr, propertyName, &newVMValue); 185 | 186 | args->result->SetBool(setOK); 187 | } 188 | }; 189 | 190 | // function GetPropertyValueEx(formIdentifier:String, scriptName:String, propertyName:String):* 191 | // Returns null if the property doesn't exist. 192 | class GetPropertyValueEx : public GFxFunctionHandler { 193 | public: 194 | virtual void Invoke(Args* args) { 195 | args->result->SetNull(); 196 | 197 | if (args->numArgs < 3) return; 198 | if (args->args[0].GetType() != GFxValue::kType_String) return; 199 | if (args->args[1].GetType() != GFxValue::kType_String) return; 200 | if (args->args[2].GetType() != GFxValue::kType_String) return; 201 | 202 | const char* formIdentifier = args->args[0].GetString(); 203 | const char* scriptName = args->args[1].GetString(); 204 | const char* propertyName = args->args[2].GetString(); 205 | 206 | VMValue valueOut; 207 | bool getOK = MCMUtils::GetPropertyValue(formIdentifier, scriptName, propertyName, &valueOut); 208 | if (getOK) 209 | PlatformAdapter::ConvertPapyrusValue(args->result, &valueOut, args->movie->movieRoot); 210 | } 211 | }; 212 | 213 | // function SetPropertyValueEx(formIdentifier:String, scriptName:String, propertyName:String, newValue:*):Boolean 214 | class SetPropertyValueEx : public GFxFunctionHandler { 215 | public: 216 | virtual void Invoke(Args* args) { 217 | args->result->SetBool(false); 218 | 219 | if (args->numArgs < 4) return; 220 | if (args->args[0].GetType() != GFxValue::kType_String) return; 221 | if (args->args[1].GetType() != GFxValue::kType_String) return; 222 | if (args->args[2].GetType() != GFxValue::kType_String) return; 223 | if (args->args[3].GetType() != GFxValue::kType_String) return; 224 | 225 | const char* formIdentifier = args->args[0].GetString(); 226 | const char* scriptName = args->args[1].GetString(); 227 | const char* propertyName = args->args[2].GetString(); 228 | GFxValue* newValue = &args->args[3]; 229 | 230 | VirtualMachine* vm = (*G::gameVM)->m_virtualMachine; 231 | 232 | VMValue newVMValue; 233 | PlatformAdapter::ConvertScaleformValue(&newVMValue, newValue, vm); 234 | bool setOK = MCMUtils::SetPropertyValue(formIdentifier, scriptName, propertyName, &newVMValue); 235 | 236 | args->result->SetBool(setOK); 237 | } 238 | }; 239 | 240 | // function CallQuestFunction(formID:String, scriptName:String, functionName:String, ...arguments); 241 | // e.g. CallQuestFunction("MyMod.esp|F99", "MyScript", "MyFunction", 0.1, 0.2, true); 242 | // Note: this function has been updated to accept any Form type. 243 | class CallQuestFunction : public GFxFunctionHandler { 244 | public: 245 | virtual void Invoke(Args* args) { 246 | args->result->SetBool(false); 247 | 248 | if (args->numArgs < 3) return; 249 | if (args->args[0].GetType() != GFxValue::kType_String) return; // formIdentifier 250 | if (args->args[1].GetType() != GFxValue::kType_String) return; // scriptName 251 | if (args->args[2].GetType() != GFxValue::kType_String) return; // functionName 252 | 253 | TESForm* targetForm = MCMUtils::GetFormFromIdentifier(args->args[0].GetString()); 254 | 255 | if (!targetForm) { 256 | _MESSAGE("WARNING: %s is not a valid form.", args->args[0].GetString()); 257 | } else { 258 | const char* scriptName = args->args[1].GetString(); 259 | 260 | VirtualMachine* vm = (*G::gameVM)->m_virtualMachine; 261 | MCMUtils::VMScript script(targetForm, scriptName); 262 | 263 | if (!script.m_identifier) { 264 | _MESSAGE("WARNING: %s cannot be resolved to a Papyrus script object.", args->args[0].GetString()); 265 | } else { 266 | BSFixedString funcName(args->args[2].GetString()); 267 | 268 | VMValue packedArgs; 269 | UInt32 length = args->numArgs - 3; 270 | VMValue::ArrayData* arrayData = nullptr; 271 | vm->CreateArray(&packedArgs, length, &arrayData); 272 | 273 | packedArgs.type.value = VMValue::kType_VariableArray; 274 | packedArgs.data.arr = arrayData; 275 | 276 | for (UInt32 i = 0; i < length; i++) 277 | { 278 | VMValue* var = new VMValue; 279 | PlatformAdapter::ConvertScaleformValue(var, &args->args[i + 3], vm); 280 | arrayData->arr.entries[i].SetVariable(var); 281 | } 282 | 283 | CallFunctionNoWait_Internal(vm, 0, script.m_identifier, &funcName, &packedArgs); 284 | 285 | args->result->SetBool(true); 286 | } 287 | } 288 | } 289 | }; 290 | 291 | // function CallGlobalFunction(scriptName:String, funcName:String, ...arguments); 292 | // e.g. CallGlobalFunction("Debug", "MessageBox", "Hello world!"); 293 | class CallGlobalFunction : public GFxFunctionHandler { 294 | public: 295 | virtual void Invoke(Args* args) { 296 | args->result->SetBool(false); 297 | 298 | if (args->numArgs < 2) return; 299 | if (args->args[0].GetType() != GFxValue::kType_String) return; 300 | if (args->args[1].GetType() != GFxValue::kType_String) return; 301 | 302 | VirtualMachine* vm = (*G::gameVM)->m_virtualMachine; 303 | 304 | BSFixedString scriptName(args->args[0].GetString()); 305 | BSFixedString funcName(args->args[1].GetString()); 306 | 307 | VMValue packedArgs; 308 | UInt32 length = args->numArgs - 2; 309 | VMValue::ArrayData* arrayData = nullptr; 310 | vm->CreateArray(&packedArgs, length, &arrayData); 311 | 312 | packedArgs.type.value = VMValue::kType_VariableArray; 313 | packedArgs.data.arr = arrayData; 314 | 315 | for (UInt32 i = 0; i < length; i++) 316 | { 317 | VMValue * var = new VMValue; 318 | PlatformAdapter::ConvertScaleformValue(var, &args->args[i + 2], vm); 319 | arrayData->arr.entries[i].SetVariable(var); 320 | } 321 | 322 | CallGlobalFunctionNoWait_Internal(vm, 0, 0, &scriptName, &funcName, &packedArgs); 323 | 324 | args->result->SetBool(true); 325 | } 326 | }; 327 | 328 | // GetModSettingInt(modName:String, settingName:String):int; 329 | class GetModSettingInt : public GFxFunctionHandler { 330 | public: 331 | virtual void Invoke(Args* args) { 332 | args->result->SetNumber(-1); 333 | 334 | if (args->numArgs != 2) return; 335 | if (args->args[0].GetType() != GFxValue::kType_String) return; 336 | if (args->args[1].GetType() != GFxValue::kType_String) return; 337 | 338 | args->result->SetInt(SettingStore::GetInstance().GetModSettingInt(args->args[0].GetString(), args->args[1].GetString())); 339 | } 340 | }; 341 | 342 | // GetModSettingBool(modName:String, settingName:String):Boolean; 343 | class GetModSettingBool : public GFxFunctionHandler { 344 | public: 345 | virtual void Invoke(Args* args) { 346 | args->result->SetBool(false); 347 | 348 | if (args->numArgs != 2) return; 349 | if (args->args[0].GetType() != GFxValue::kType_String) return; 350 | if (args->args[1].GetType() != GFxValue::kType_String) return; 351 | 352 | args->result->SetBool(SettingStore::GetInstance().GetModSettingBool(args->args[0].GetString(), args->args[1].GetString())); 353 | } 354 | }; 355 | 356 | // GetModSettingFloat(modName:String, settingName:String):Number; 357 | class GetModSettingFloat : public GFxFunctionHandler { 358 | public: 359 | virtual void Invoke(Args* args) { 360 | args->result->SetNumber(-1); 361 | 362 | if (args->numArgs != 2) return; 363 | if (args->args[0].GetType() != GFxValue::kType_String) return; 364 | if (args->args[1].GetType() != GFxValue::kType_String) return; 365 | 366 | args->result->SetNumber(SettingStore::GetInstance().GetModSettingFloat(args->args[0].GetString(), args->args[1].GetString())); 367 | } 368 | }; 369 | 370 | // GetModSettingString(modName:String, settingName:String):String; 371 | class GetModSettingString : public GFxFunctionHandler { 372 | public: 373 | virtual void Invoke(Args* args) { 374 | args->result->SetString(""); 375 | 376 | if (args->numArgs != 2) return; 377 | if (args->args[0].GetType() != GFxValue::kType_String) return; 378 | if (args->args[1].GetType() != GFxValue::kType_String) return; 379 | 380 | args->result->SetString(SettingStore::GetInstance().GetModSettingString(args->args[0].GetString(), args->args[1].GetString())); 381 | } 382 | }; 383 | 384 | // SetModSettingInt(modName:String, settingName:String, value:int):Boolean; 385 | class SetModSettingInt : public GFxFunctionHandler { 386 | public: 387 | virtual void Invoke(Args* args) { 388 | args->result->SetBool(false); 389 | 390 | if (args->numArgs != 3) return; 391 | if (args->args[0].GetType() != GFxValue::kType_String) return; 392 | if (args->args[1].GetType() != GFxValue::kType_String) return; 393 | if (args->args[2].GetType() != GFxValue::kType_Int) return; 394 | 395 | SettingStore::GetInstance().SetModSettingInt(args->args[0].GetString(), args->args[1].GetString(), args->args[2].GetInt()); 396 | 397 | args->result->SetBool(true); 398 | } 399 | }; 400 | 401 | // SetModSettingBool(modName:String, settingName:String, value:Boolean):Boolean; 402 | class SetModSettingBool : public GFxFunctionHandler { 403 | public: 404 | virtual void Invoke(Args* args) { 405 | args->result->SetBool(false); 406 | 407 | if (args->numArgs != 3) return; 408 | if (args->args[0].GetType() != GFxValue::kType_String) return; 409 | if (args->args[1].GetType() != GFxValue::kType_String) return; 410 | if (args->args[2].GetType() != GFxValue::kType_Bool) return; 411 | 412 | SettingStore::GetInstance().SetModSettingBool(args->args[0].GetString(), args->args[1].GetString(), args->args[2].GetBool()); 413 | 414 | args->result->SetBool(true); 415 | } 416 | }; 417 | 418 | // SetModSettingFloat(modName:String, settingName:String, value:Number):Boolean; 419 | class SetModSettingFloat : public GFxFunctionHandler { 420 | public: 421 | virtual void Invoke(Args* args) { 422 | args->result->SetBool(false); 423 | 424 | if (args->numArgs != 3) return; 425 | if (args->args[0].GetType() != GFxValue::kType_String) return; 426 | if (args->args[1].GetType() != GFxValue::kType_String) return; 427 | if (args->args[2].GetType() != GFxValue::kType_Number) return; 428 | 429 | SettingStore::GetInstance().SetModSettingFloat(args->args[0].GetString(), args->args[1].GetString(), args->args[2].GetNumber()); 430 | 431 | args->result->SetBool(true); 432 | } 433 | }; 434 | 435 | // SetModSettingString(modName:String, settingName:String, value:String):Boolean; 436 | class SetModSettingString : public GFxFunctionHandler { 437 | public: 438 | virtual void Invoke(Args* args) { 439 | args->result->SetBool(false); 440 | 441 | if (args->numArgs != 3) return; 442 | if (args->args[0].GetType() != GFxValue::kType_String) return; 443 | if (args->args[1].GetType() != GFxValue::kType_String) return; 444 | if (args->args[2].GetType() != GFxValue::kType_String) return; 445 | 446 | SettingStore::GetInstance().SetModSettingString(args->args[0].GetString(), args->args[1].GetString(), args->args[2].GetString()); 447 | 448 | args->result->SetBool(true); 449 | } 450 | }; 451 | 452 | // IsPluginInstalled(modName:String):Boolean; 453 | class IsPluginInstalled : public GFxFunctionHandler { 454 | public: 455 | virtual void Invoke(Args* args) { 456 | args->result->SetBool(false); 457 | 458 | if (args->numArgs < 1) return; 459 | if (args->args[0].GetType() != GFxValue::kType_String) return; 460 | 461 | const ModInfo* mi = (*G::dataHandler)->LookupModByName(args->args[0].GetString()); 462 | // modIndex == -1 for mods that are present in the Data directory but not active. 463 | if (mi && mi->modIndex != 0xFF) { 464 | args->result->SetBool(true); 465 | } 466 | } 467 | }; 468 | 469 | class GetKeybind : public GFxFunctionHandler { 470 | public: 471 | enum CallType { 472 | kCallType_ID, 473 | kCallType_Keycode, 474 | }; 475 | 476 | virtual void Invoke(Args* args) { 477 | CallType callType = kCallType_ID; 478 | 479 | if (args->numArgs < 2) return; 480 | if (args->args[0].GetType() == GFxValue::kType_String && args->args[1].GetType() == GFxValue::kType_String) { 481 | callType = kCallType_ID; 482 | } else if (args->args[0].GetType() == GFxValue::kType_Int && args->args[1].GetType() == GFxValue::kType_Int) { 483 | callType = kCallType_Keycode; 484 | } else { 485 | // Invalid parameter type 486 | return; 487 | } 488 | 489 | GFxValue keycode, modifiers, keybindType, keybindID, keybindName, modName, type, flags, targetForm, callbackName; 490 | 491 | g_keybindManager.Lock(); 492 | KeybindInfo ki; 493 | if (callType == kCallType_ID) { 494 | ki = g_keybindManager.GetKeybind(args->args[0].GetString(), args->args[1].GetString()); 495 | } else { 496 | Keybind kb = {}; 497 | kb.keycode = args->args[0].GetInt(); 498 | kb.modifiers = args->args[1].GetInt(); 499 | ki = g_keybindManager.GetKeybind(kb); 500 | } 501 | g_keybindManager.Release(); 502 | 503 | SetKeybindInfo(ki, args->movie->movieRoot, args->result); 504 | } 505 | }; 506 | 507 | class GetAllKeybinds : public GFxFunctionHandler { 508 | public: 509 | virtual void Invoke(Args* args) { 510 | g_keybindManager.Lock(); 511 | std::vector keybinds = g_keybindManager.GetAllKeybinds(); 512 | g_keybindManager.Release(); 513 | 514 | args->movie->movieRoot->CreateArray(args->result); 515 | 516 | for (int i = 0; i < keybinds.size(); i++) { 517 | GFxValue keybindInfoValue; 518 | SetKeybindInfo(keybinds[i], args->movie->movieRoot, &keybindInfoValue); 519 | args->result->PushBack(&keybindInfoValue); 520 | } 521 | } 522 | }; 523 | 524 | // function SetKeybind(modName:String, keybindID:String, keycode:int, modifiers:int):Boolean 525 | class SetKeybind : public GFxFunctionHandler { 526 | public: 527 | virtual void Invoke(Args* args) { 528 | args->result->SetBool(false); 529 | 530 | if (args->numArgs < 4) return; 531 | if (args->args[0].GetType() != GFxValue::kType_String) return; // modName 532 | if (args->args[1].GetType() != GFxValue::kType_String) return; // keybindID 533 | if (args->args[2].GetType() != GFxValue::kType_Int) return; // keycode 534 | if (args->args[3].GetType() != GFxValue::kType_Int) return; // modifiers 535 | 536 | std::string modName = args->args[0].GetString(); 537 | std::string keybindID = args->args[1].GetString(); 538 | 539 | Keybind kb = {}; 540 | kb.keycode = args->args[2].GetInt(); 541 | kb.modifiers = args->args[3].GetInt(); 542 | 543 | if (g_keybindManager.RegisterKeybind(kb, modName.c_str(), keybindID.c_str())) { 544 | args->result->SetBool(true); 545 | } 546 | } 547 | }; 548 | 549 | // function SetKeybindEx(modName:String, keybindID:String, keybindName:String, keycode:int, modifiers:int, type:int, params:Array) 550 | class SetKeybindEx : public GFxFunctionHandler { 551 | public: 552 | virtual void Invoke(Args* args) { 553 | if (args->numArgs < 5) return; 554 | if (args->args[0].GetType() != GFxValue::kType_String) return; // modName 555 | if (args->args[1].GetType() != GFxValue::kType_String) return; // keybindID 556 | if (args->args[2].GetType() != GFxValue::kType_String) return; // keybindDesc 557 | if (args->args[3].GetType() != GFxValue::kType_Int) return; // keycode 558 | if (args->args[4].GetType() != GFxValue::kType_Int) return; // modifiers 559 | if (args->args[5].GetType() != GFxValue::kType_Int) return; // type 560 | if (args->args[6].GetType() != GFxValue::kType_Array) return; // params 561 | 562 | Keybind kb = {}; 563 | KeybindParameters kp = {}; 564 | kp.modName = args->args[0].GetString(); 565 | kp.keybindID = args->args[1].GetString(); 566 | kp.keybindDesc = args->args[2].GetString(); 567 | kb.keycode = args->args[3].GetInt(); 568 | kb.modifiers = args->args[4].GetInt(); 569 | kp.type = args->args[5].GetInt(); 570 | GFxValue params = args->args[6]; 571 | int paramSize = params.GetArraySize(); 572 | 573 | switch (kp.type) { 574 | case KeybindParameters::kType_CallFunction: 575 | { 576 | if (paramSize < 2) return; 577 | GFxValue targetFormIdentifier, callbackName; 578 | params.GetElement(0, &targetFormIdentifier); 579 | params.GetElement(1, &callbackName); 580 | 581 | if (targetFormIdentifier.GetType() != GFxValue::kType_String) return; 582 | if (callbackName.GetType() != GFxValue::kType_String) return; 583 | 584 | TESForm* targetForm = MCMUtils::GetFormFromIdentifier(targetFormIdentifier.GetString()); 585 | if (!targetForm) { 586 | _WARNING("Cannot register a None form as a call target."); 587 | return; 588 | } 589 | 590 | kp.targetFormID = targetForm->formID; 591 | kp.callbackName = callbackName.GetString(); 592 | 593 | g_keybindManager.Register(kb, kp); 594 | 595 | _MESSAGE("Succesfully registered kType_CallFunction keybind for keycode %d.", kb.keycode); 596 | 597 | break; 598 | } 599 | case KeybindParameters::kType_CallGlobalFunction: 600 | { 601 | if (paramSize < 2) return; 602 | GFxValue scriptName, functionName; 603 | params.GetElement(0, &scriptName); 604 | params.GetElement(1, &functionName); 605 | 606 | if (scriptName.GetType() != GFxValue::kType_String) return; 607 | if (functionName.GetType() != GFxValue::kType_String) return; 608 | 609 | kp.scriptName = scriptName.GetString(); 610 | kp.callbackName = functionName.GetString(); 611 | 612 | g_keybindManager.Register(kb, kp); 613 | 614 | _MESSAGE("Succesfully registered kType_CallGlobalFunction keybind for keycode %d.", kb.keycode); 615 | 616 | break; 617 | } 618 | case KeybindParameters::kType_RunConsoleCommand: 619 | { 620 | if (paramSize < 1) return; 621 | GFxValue consoleCommandValue; 622 | params.GetElement(0, &consoleCommandValue); 623 | 624 | if (consoleCommandValue.GetType() != GFxValue::kType_String) return; 625 | 626 | kp.callbackName = consoleCommandValue.GetString(); 627 | 628 | g_keybindManager.Register(kb, kp); 629 | 630 | _MESSAGE("Succesfully registered kType_RunConsoleCommand keybind for keycode %d.", kb.keycode); 631 | 632 | break; 633 | } 634 | case KeybindParameters::kType_SendEvent: 635 | { 636 | _MESSAGE("Not implemented."); 637 | break; 638 | } 639 | default: 640 | _WARNING("Failed to register keybind. Unknown keybind type."); 641 | } 642 | } 643 | }; 644 | 645 | class ClearKeybind : public GFxFunctionHandler { 646 | public: 647 | enum CallType { 648 | kCallType_ID, 649 | kCallType_Keycode, 650 | }; 651 | 652 | virtual void Invoke(Args* args) { 653 | args->result->SetBool(false); 654 | 655 | CallType callType = kCallType_ID; 656 | if (args->numArgs < 2) return; 657 | if (args->args[0].GetType() == GFxValue::kType_String && args->args[1].GetType() == GFxValue::kType_String) { 658 | callType = kCallType_ID; 659 | } else if (args->args[0].GetType() == GFxValue::kType_Int && args->args[1].GetType() == GFxValue::kType_Int) { 660 | callType = kCallType_Keycode; 661 | } else { 662 | // Invalid parameter type 663 | return; 664 | } 665 | 666 | g_keybindManager.Lock(); 667 | if (callType == kCallType_ID) { 668 | if (g_keybindManager.ClearKeybind(args->args[0].GetString(), args->args[1].GetString())) 669 | args->result->SetBool(true); 670 | } else { 671 | Keybind kb = {}; 672 | kb.keycode = args->args[0].GetInt(); 673 | kb.modifiers = args->args[1].GetInt(); 674 | if (g_keybindManager.ClearKeybind(kb)) 675 | args->result->SetBool(true); 676 | } 677 | g_keybindManager.Release(); 678 | } 679 | }; 680 | 681 | class RemapKeybind : public GFxFunctionHandler { 682 | public: 683 | virtual void Invoke(Args* args) { 684 | args->result->SetBool(false); 685 | 686 | if (args->numArgs < 4) return; 687 | if (args->args[0].GetType() != GFxValue::kType_String) return; // modName 688 | if (args->args[1].GetType() != GFxValue::kType_String) return; // keybindID 689 | if (args->args[2].GetType() != GFxValue::kType_Int) return; // newKeycode 690 | if (args->args[3].GetType() != GFxValue::kType_Int) return; // newModifiers 691 | 692 | Keybind kb = {}; 693 | kb.keycode = args->args[2].GetInt(); 694 | kb.modifiers = args->args[3].GetInt(); 695 | 696 | g_keybindManager.Lock(); 697 | if (g_keybindManager.RemapKeybind(args->args[0].GetString(), args->args[1].GetString(), kb)) 698 | args->result->SetBool(true); 699 | g_keybindManager.Release(); 700 | } 701 | }; 702 | 703 | class GetFullName : public GFxFunctionHandler { 704 | public: 705 | virtual void Invoke(Args* args) { 706 | args->result->SetString(""); 707 | if (args->args[0].GetType() != GFxValue::kType_String) return; 708 | TESForm* form = MCMUtils::GetFormFromIdentifier(args->args[0].GetString()); 709 | if (!form) return; 710 | TESFullName* fullname = DYNAMIC_CAST(form, TESForm, TESFullName); 711 | if (!fullname) return; 712 | args->result->SetString(fullname->name.c_str()); 713 | } 714 | }; 715 | 716 | class GetDescription : public GFxFunctionHandler { 717 | public: 718 | virtual void Invoke(Args* args) { 719 | args->result->SetString(""); 720 | if (args->args[0].GetType() != GFxValue::kType_String) return; 721 | TESForm* form = MCMUtils::GetFormFromIdentifier(args->args[0].GetString()); 722 | if (!form) return; 723 | args->result->SetString(MCMUtils::GetDescription(form).c_str()); 724 | } 725 | }; 726 | 727 | // function GetListFromForm(formIdentifier:String):Array 728 | class GetListFromForm : public GFxFunctionHandler { 729 | public: 730 | virtual void Invoke(Args* args) { 731 | args->movie->movieRoot->CreateArray(args->result); 732 | 733 | if (args->numArgs < 1) return; 734 | if (args->args[0].GetType() != GFxValue::kType_String) return; // formIdentifier 735 | 736 | TESForm* form = MCMUtils::GetFormFromIdentifier(args->args[0].GetString()); 737 | if (!form) return; 738 | BGSListForm* formlist = DYNAMIC_CAST(form, TESForm, BGSListForm); 739 | if (!formlist) return; 740 | 741 | for (size_t i = 0; i < formlist->forms.count; i++) 742 | { 743 | form = formlist->forms.entries[i]; 744 | GFxValue value; 745 | args->movie->movieRoot->CreateString(&value, ""); 746 | TESFullName* fullname = DYNAMIC_CAST(form, TESForm, TESFullName); 747 | value.SetString(fullname ? fullname->name.c_str() : form->GetEditorID()); 748 | args->result->PushBack(&value); 749 | } 750 | } 751 | }; 752 | } 753 | 754 | void ScaleformMCM::RegisterFuncs(GFxValue* codeObj, GFxMovieRoot* movieRoot) { 755 | // MCM Data 756 | RegisterFunction(codeObj, movieRoot, "GetMCMVersionString"); 757 | RegisterFunction(codeObj, movieRoot, "GetMCMVersionCode"); 758 | RegisterFunction(codeObj, movieRoot, "GetConfigList"); 759 | 760 | // MCM Events 761 | RegisterFunction(codeObj, movieRoot, "OnMCMOpen"); 762 | RegisterFunction(codeObj, movieRoot, "OnMCMClose"); 763 | 764 | // MCM Utilities 765 | RegisterFunction(codeObj, movieRoot, "DisableMenuInput"); 766 | 767 | // Actions 768 | RegisterFunction(codeObj, movieRoot, "GetGlobalValue"); 769 | RegisterFunction(codeObj, movieRoot, "SetGlobalValue"); 770 | RegisterFunction(codeObj, movieRoot, "GetPropertyValue"); 771 | RegisterFunction(codeObj, movieRoot, "SetPropertyValue"); 772 | RegisterFunction(codeObj, movieRoot, "GetPropertyValueEx"); 773 | RegisterFunction(codeObj, movieRoot, "SetPropertyValueEx"); 774 | RegisterFunction(codeObj, movieRoot, "CallQuestFunction"); 775 | RegisterFunction(codeObj, movieRoot, "CallGlobalFunction"); 776 | 777 | // Mod Settings 778 | RegisterFunction(codeObj, movieRoot, "GetModSettingInt"); 779 | RegisterFunction(codeObj, movieRoot, "GetModSettingBool"); 780 | RegisterFunction(codeObj, movieRoot, "GetModSettingFloat"); 781 | RegisterFunction(codeObj, movieRoot, "GetModSettingString"); 782 | 783 | RegisterFunction(codeObj, movieRoot, "SetModSettingInt"); 784 | RegisterFunction(codeObj, movieRoot, "SetModSettingBool"); 785 | RegisterFunction(codeObj, movieRoot, "SetModSettingFloat"); 786 | RegisterFunction(codeObj, movieRoot, "SetModSettingString"); 787 | 788 | // Mod Info 789 | RegisterFunction(codeObj, movieRoot, "IsPluginInstalled"); 790 | 791 | // Keybinds 792 | RegisterFunction(codeObj, movieRoot, "GetKeybind"); 793 | RegisterFunction(codeObj, movieRoot, "GetAllKeybinds"); 794 | RegisterFunction(codeObj, movieRoot, "SetKeybind"); 795 | RegisterFunction(codeObj, movieRoot, "ClearKeybind"); 796 | RegisterFunction(codeObj, movieRoot, "RemapKeybind"); 797 | 798 | // 799 | RegisterFunction(codeObj, movieRoot, "GetFullName"); 800 | RegisterFunction(codeObj, movieRoot, "GetDescription"); 801 | RegisterFunction(codeObj, movieRoot, "GetListFromForm"); 802 | } 803 | 804 | //------------------------- 805 | // Input Handler 806 | //------------------------- 807 | class F4SEInputHandler : public BSInputEventUser 808 | { 809 | public: 810 | F4SEInputHandler() : BSInputEventUser(true) { } 811 | 812 | virtual void OnButtonEvent(ButtonEvent * inputEvent) 813 | { 814 | UInt32 keyCode; 815 | UInt32 deviceType = inputEvent->deviceType; 816 | UInt32 keyMask = inputEvent->keyMask; 817 | 818 | if (deviceType == InputEvent::kDeviceType_Mouse) { 819 | // Mouse 820 | if (keyMask < 2 || keyMask > 7) return; // Disallow Mouse1, Mouse2, MouseWheelUp and MouseWheelDown 821 | keyCode = InputMap::kMacro_MouseButtonOffset + keyMask; 822 | } else if (deviceType == InputEvent::kDeviceType_Gamepad) { 823 | // Gamepad 824 | keyCode = InputMap::GamepadMaskToKeycode(keyMask); 825 | } else { 826 | // Keyboard 827 | keyCode = keyMask; 828 | } 829 | 830 | float timer = inputEvent->timer; 831 | bool isDown = inputEvent->isDown == 1.0f && timer == 0.0f; 832 | bool isUp = inputEvent->isDown == 0.0f && timer != 0.0f; 833 | 834 | BSFixedString* control = inputEvent->GetControlID(); 835 | 836 | if (isDown) { 837 | ScaleformMCM::ProcessKeyEvent(keyCode, true); 838 | ScaleformMCM::ProcessUserEvent(control->c_str(), true, deviceType); 839 | } else if (isUp) { 840 | ScaleformMCM::ProcessKeyEvent(keyCode, false); 841 | ScaleformMCM::ProcessUserEvent(control->c_str(), false, deviceType); 842 | } 843 | } 844 | }; 845 | F4SEInputHandler g_scaleformInputHandler; 846 | 847 | void ScaleformMCM::ProcessKeyEvent(UInt32 keyCode, bool isDown) 848 | { 849 | BSFixedString mainMenuStr("PauseMenu"); 850 | if ((*G::ui)->IsMenuOpen(mainMenuStr)) { 851 | IMenu* menu = (*G::ui)->GetMenu(mainMenuStr); 852 | GFxMovieRoot* movieRoot = menu->movie->movieRoot; 853 | GFxValue args[2]; 854 | args[0].SetInt(keyCode); 855 | args[1].SetBool(isDown); 856 | movieRoot->Invoke("root.mcm_loader.content.ProcessKeyEvent", nullptr, args, 2); 857 | } 858 | } 859 | 860 | void ScaleformMCM::ProcessUserEvent(const char * controlName, bool isDown, int deviceType) 861 | { 862 | BSFixedString mainMenuStr("PauseMenu"); 863 | if ((*G::ui)->IsMenuOpen(mainMenuStr)) { 864 | IMenu* menu = (*G::ui)->GetMenu(mainMenuStr); 865 | GFxMovieRoot* movieRoot = menu->movie->movieRoot; 866 | GFxValue args[3]; 867 | args[0].SetString(controlName); 868 | args[1].SetBool(isDown); 869 | args[2].SetInt(deviceType); 870 | movieRoot->Invoke("root.mcm_loader.content.ProcessUserEvent", nullptr, args, 3); 871 | } 872 | } 873 | 874 | void ScaleformMCM::RefreshMenu() 875 | { 876 | BSFixedString mainMenuStr("PauseMenu"); 877 | if ((*G::ui)->IsMenuOpen(mainMenuStr)) { 878 | IMenu* menu = (*G::ui)->GetMenu(mainMenuStr); 879 | GFxMovieRoot* movieRoot = menu->movie->movieRoot; 880 | movieRoot->Invoke("root.mcm_loader.content.RefreshMCM", nullptr, nullptr, 0); 881 | } 882 | } 883 | 884 | void ScaleformMCM::SetKeybindInfo(KeybindInfo ki, GFxMovieRoot * movieRoot, GFxValue * kiValue) 885 | { 886 | GFxValue keycode, modifiers, keybindType, keybindID, keybindName, modName, type, flags, targetForm, callbackName; 887 | 888 | keycode.SetInt(ki.keycode); 889 | modifiers.SetInt(ki.modifiers); 890 | keybindType.SetInt(ki.keybindType); 891 | keybindID.SetString(ki.keybindID); 892 | keybindName.SetString(ki.keybindDesc); 893 | modName.SetString(ki.modName); 894 | type.SetInt(ki.type); 895 | flags.SetInt(ki.flags); 896 | targetForm.SetString(ki.callTarget); 897 | callbackName.SetString(ki.callbackName); 898 | 899 | movieRoot->CreateObject(kiValue); 900 | kiValue->SetMember("keycode", &keycode); 901 | kiValue->SetMember("modifiers", &modifiers); 902 | kiValue->SetMember("keybindType", &keybindType); 903 | kiValue->SetMember("keybindID", &keybindID); 904 | kiValue->SetMember("keybindName", &keybindName); 905 | kiValue->SetMember("modName", &modName); 906 | kiValue->SetMember("type", &type); 907 | kiValue->SetMember("flags", &flags); 908 | kiValue->SetMember("targetForm", &targetForm); 909 | kiValue->SetMember("callbackName", &callbackName); 910 | } 911 | 912 | void ScaleformMCM::RegisterForInput(bool bRegister) { 913 | if (bRegister) { 914 | g_scaleformInputHandler.enabled = true; 915 | tArray* inputEvents = &((*G::menuControls)->inputEvents); 916 | BSInputEventUser* inputHandler = &g_scaleformInputHandler; 917 | int idx = inputEvents->GetItemIndex(inputHandler); 918 | if (idx == -1) { 919 | inputEvents->Push(&g_scaleformInputHandler); 920 | _MESSAGE("Registered for input events."); 921 | } 922 | } else { 923 | g_scaleformInputHandler.enabled = false; 924 | } 925 | } 926 | 927 | bool ScaleformMCM::RegisterScaleform(GFxMovieView * view, GFxValue * f4se_root) 928 | { 929 | GFxMovieRoot* movieRoot = view->movieRoot; 930 | 931 | GFxValue currentSWFPath; 932 | const char* currentSWFPathString = nullptr; 933 | 934 | if (movieRoot->GetVariable(¤tSWFPath, "root.loaderInfo.url")) { 935 | currentSWFPathString = currentSWFPath.GetString(); 936 | } else { 937 | _MESSAGE("WARNING: Scaleform registration failed."); 938 | } 939 | 940 | // Look for the menu that we want to inject into. 941 | if (strcmp(currentSWFPathString, "Interface/MainMenu.swf") == 0) { 942 | GFxValue root; movieRoot->GetVariable(&root, "root"); 943 | 944 | // Register native code object 945 | GFxValue mcm; movieRoot->CreateObject(&mcm); 946 | root.SetMember("mcm", &mcm); 947 | ScaleformMCM::RegisterFuncs(&mcm, movieRoot); 948 | 949 | // Inject MCM menu 950 | GFxValue loader, urlRequest; 951 | movieRoot->CreateObject(&loader, "flash.display.Loader"); 952 | movieRoot->CreateObject(&urlRequest, "flash.net.URLRequest", &GFxValue("MCM.swf"), 1); 953 | 954 | root.SetMember("mcm_loader", &loader); 955 | bool injectionSuccess = movieRoot->Invoke("root.mcm_loader.load", nullptr, &urlRequest, 1); 956 | 957 | movieRoot->Invoke("root.Menu_mc.addChild", nullptr, &loader, 1); 958 | 959 | if (!injectionSuccess) { 960 | _MESSAGE("WARNING: MCM injection failed."); 961 | } 962 | } 963 | 964 | return true; 965 | } -------------------------------------------------------------------------------- /src/json/json.h: -------------------------------------------------------------------------------- 1 | /// Json-cpp amalgated header (http://jsoncpp.sourceforge.net/). 2 | /// It is intended to be used with #include "json/json.h" 3 | 4 | // ////////////////////////////////////////////////////////////////////// 5 | // Beginning of content of file: LICENSE 6 | // ////////////////////////////////////////////////////////////////////// 7 | 8 | /* 9 | The JsonCpp library's source code, including accompanying documentation, 10 | tests and demonstration applications, are licensed under the following 11 | conditions... 12 | 13 | The JsonCpp Authors explicitly disclaim copyright in all 14 | jurisdictions which recognize such a disclaimer. In such jurisdictions, 15 | this software is released into the Public Domain. 16 | 17 | In jurisdictions which do not recognize Public Domain property (e.g. Germany as of 18 | 2010), this software is Copyright (c) 2007-2010 by The JsonCpp Authors, and is 19 | released under the terms of the MIT License (see below). 20 | 21 | In jurisdictions which recognize Public Domain property, the user of this 22 | software may choose to accept it either as 1) Public Domain, 2) under the 23 | conditions of the MIT License (see below), or 3) under the terms of dual 24 | Public Domain/MIT License conditions described here, as they choose. 25 | 26 | The MIT License is about as close to Public Domain as a license can get, and is 27 | described in clear, concise terms at: 28 | 29 | http://en.wikipedia.org/wiki/MIT_License 30 | 31 | The full text of the MIT License follows: 32 | 33 | ======================================================================== 34 | Copyright (c) 2007-2010 The JsonCpp Authors 35 | 36 | Permission is hereby granted, free of charge, to any person 37 | obtaining a copy of this software and associated documentation 38 | files (the "Software"), to deal in the Software without 39 | restriction, including without limitation the rights to use, copy, 40 | modify, merge, publish, distribute, sublicense, and/or sell copies 41 | of the Software, and to permit persons to whom the Software is 42 | furnished to do so, subject to the following conditions: 43 | 44 | The above copyright notice and this permission notice shall be 45 | included in all copies or substantial portions of the Software. 46 | 47 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 48 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 49 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 50 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 51 | BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 52 | ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 53 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 54 | SOFTWARE. 55 | ======================================================================== 56 | (END LICENSE TEXT) 57 | 58 | The MIT license is compatible with both the GPL and commercial 59 | software, affording one all of the rights of Public Domain with the 60 | minor nuisance of being required to keep the above copyright notice 61 | and license text in the source code. Note also that by accepting the 62 | Public Domain "license" you can re-license your copy using whatever 63 | license you like. 64 | 65 | */ 66 | 67 | // ////////////////////////////////////////////////////////////////////// 68 | // End of content of file: LICENSE 69 | // ////////////////////////////////////////////////////////////////////// 70 | 71 | 72 | 73 | 74 | 75 | #ifndef JSON_AMALGATED_H_INCLUDED 76 | # define JSON_AMALGATED_H_INCLUDED 77 | /// If defined, indicates that the source file is amalgated 78 | /// to prevent private header inclusion. 79 | #define JSON_IS_AMALGAMATION 80 | 81 | // ////////////////////////////////////////////////////////////////////// 82 | // Beginning of content of file: include/json/version.h 83 | // ////////////////////////////////////////////////////////////////////// 84 | 85 | // DO NOT EDIT. This file (and "version") is generated by CMake. 86 | // Run CMake configure step to update it. 87 | #ifndef JSON_VERSION_H_INCLUDED 88 | # define JSON_VERSION_H_INCLUDED 89 | 90 | # define JSONCPP_VERSION_STRING "1.8.1" 91 | # define JSONCPP_VERSION_MAJOR 1 92 | # define JSONCPP_VERSION_MINOR 8 93 | # define JSONCPP_VERSION_PATCH 1 94 | # define JSONCPP_VERSION_QUALIFIER 95 | # define JSONCPP_VERSION_HEXA ((JSONCPP_VERSION_MAJOR << 24) | (JSONCPP_VERSION_MINOR << 16) | (JSONCPP_VERSION_PATCH << 8)) 96 | 97 | #ifdef JSONCPP_USING_SECURE_MEMORY 98 | #undef JSONCPP_USING_SECURE_MEMORY 99 | #endif 100 | #define JSONCPP_USING_SECURE_MEMORY 0 101 | // If non-zero, the library zeroes any memory that it has allocated before 102 | // it frees its memory. 103 | 104 | #endif // JSON_VERSION_H_INCLUDED 105 | 106 | // ////////////////////////////////////////////////////////////////////// 107 | // End of content of file: include/json/version.h 108 | // ////////////////////////////////////////////////////////////////////// 109 | 110 | 111 | 112 | 113 | 114 | 115 | // ////////////////////////////////////////////////////////////////////// 116 | // Beginning of content of file: include/json/config.h 117 | // ////////////////////////////////////////////////////////////////////// 118 | 119 | // Copyright 2007-2010 Baptiste Lepilleur 120 | // Distributed under MIT license, or public domain if desired and 121 | // recognized in your jurisdiction. 122 | // See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE 123 | 124 | #ifndef JSON_CONFIG_H_INCLUDED 125 | #define JSON_CONFIG_H_INCLUDED 126 | #include 127 | #include //typedef String 128 | #include //typedef int64_t, uint64_t 129 | 130 | /// If defined, indicates that json library is embedded in CppTL library. 131 | //# define JSON_IN_CPPTL 1 132 | 133 | /// If defined, indicates that json may leverage CppTL library 134 | //# define JSON_USE_CPPTL 1 135 | /// If defined, indicates that cpptl vector based map should be used instead of 136 | /// std::map 137 | /// as Value container. 138 | //# define JSON_USE_CPPTL_SMALLMAP 1 139 | 140 | // If non-zero, the library uses exceptions to report bad input instead of C 141 | // assertion macros. The default is to use exceptions. 142 | #ifndef JSON_USE_EXCEPTION 143 | #define JSON_USE_EXCEPTION 1 144 | #endif 145 | 146 | /// If defined, indicates that the source file is amalgated 147 | /// to prevent private header inclusion. 148 | /// Remarks: it is automatically defined in the generated amalgated header. 149 | // #define JSON_IS_AMALGAMATION 150 | 151 | #ifdef JSON_IN_CPPTL 152 | #include 153 | #ifndef JSON_USE_CPPTL 154 | #define JSON_USE_CPPTL 1 155 | #endif 156 | #endif 157 | 158 | #ifdef JSON_IN_CPPTL 159 | #define JSON_API CPPTL_API 160 | #elif defined(JSON_DLL_BUILD) 161 | #if defined(_MSC_VER) || defined(__MINGW32__) 162 | #define JSON_API __declspec(dllexport) 163 | #define JSONCPP_DISABLE_DLL_INTERFACE_WARNING 164 | #endif // if defined(_MSC_VER) 165 | #elif defined(JSON_DLL) 166 | #if defined(_MSC_VER) || defined(__MINGW32__) 167 | #define JSON_API __declspec(dllimport) 168 | #define JSONCPP_DISABLE_DLL_INTERFACE_WARNING 169 | #endif // if defined(_MSC_VER) 170 | #endif // ifdef JSON_IN_CPPTL 171 | #if !defined(JSON_API) 172 | #define JSON_API 173 | #endif 174 | 175 | // If JSON_NO_INT64 is defined, then Json only support C++ "int" type for 176 | // integer 177 | // Storages, and 64 bits integer support is disabled. 178 | // #define JSON_NO_INT64 1 179 | 180 | #if defined(_MSC_VER) // MSVC 181 | # if _MSC_VER <= 1200 // MSVC 6 182 | // Microsoft Visual Studio 6 only support conversion from __int64 to double 183 | // (no conversion from unsigned __int64). 184 | # define JSON_USE_INT64_DOUBLE_CONVERSION 1 185 | // Disable warning 4786 for VS6 caused by STL (identifier was truncated to '255' 186 | // characters in the debug information) 187 | // All projects I've ever seen with VS6 were using this globally (not bothering 188 | // with pragma push/pop). 189 | # pragma warning(disable : 4786) 190 | # endif // MSVC 6 191 | 192 | # if _MSC_VER >= 1500 // MSVC 2008 193 | /// Indicates that the following function is deprecated. 194 | # define JSONCPP_DEPRECATED(message) __declspec(deprecated(message)) 195 | # endif 196 | 197 | #endif // defined(_MSC_VER) 198 | 199 | // In c++11 the override keyword allows you to explicity define that a function 200 | // is intended to override the base-class version. This makes the code more 201 | // managable and fixes a set of common hard-to-find bugs. 202 | #if __cplusplus >= 201103L 203 | # define JSONCPP_OVERRIDE override 204 | # define JSONCPP_NOEXCEPT noexcept 205 | #elif defined(_MSC_VER) && _MSC_VER > 1600 && _MSC_VER < 1900 206 | # define JSONCPP_OVERRIDE override 207 | # define JSONCPP_NOEXCEPT throw() 208 | #elif defined(_MSC_VER) && _MSC_VER >= 1900 209 | # define JSONCPP_OVERRIDE override 210 | # define JSONCPP_NOEXCEPT noexcept 211 | #else 212 | # define JSONCPP_OVERRIDE 213 | # define JSONCPP_NOEXCEPT throw() 214 | #endif 215 | 216 | #ifndef JSON_HAS_RVALUE_REFERENCES 217 | 218 | #if defined(_MSC_VER) && _MSC_VER >= 1600 // MSVC >= 2010 219 | #define JSON_HAS_RVALUE_REFERENCES 1 220 | #endif // MSVC >= 2010 221 | 222 | #ifdef __clang__ 223 | #if __has_feature(cxx_rvalue_references) 224 | #define JSON_HAS_RVALUE_REFERENCES 1 225 | #endif // has_feature 226 | 227 | #elif defined __GNUC__ // not clang (gcc comes later since clang emulates gcc) 228 | #if defined(__GXX_EXPERIMENTAL_CXX0X__) || (__cplusplus >= 201103L) 229 | #define JSON_HAS_RVALUE_REFERENCES 1 230 | #endif // GXX_EXPERIMENTAL 231 | 232 | #endif // __clang__ || __GNUC__ 233 | 234 | #endif // not defined JSON_HAS_RVALUE_REFERENCES 235 | 236 | #ifndef JSON_HAS_RVALUE_REFERENCES 237 | #define JSON_HAS_RVALUE_REFERENCES 0 238 | #endif 239 | 240 | #ifdef __clang__ 241 | #elif defined __GNUC__ // not clang (gcc comes later since clang emulates gcc) 242 | # if (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5)) 243 | # define JSONCPP_DEPRECATED(message) __attribute__ ((deprecated(message))) 244 | # elif (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1)) 245 | # define JSONCPP_DEPRECATED(message) __attribute__((__deprecated__)) 246 | # endif // GNUC version 247 | #endif // __clang__ || __GNUC__ 248 | 249 | #if !defined(JSONCPP_DEPRECATED) 250 | #define JSONCPP_DEPRECATED(message) 251 | #endif // if !defined(JSONCPP_DEPRECATED) 252 | 253 | #if __GNUC__ >= 6 254 | # define JSON_USE_INT64_DOUBLE_CONVERSION 1 255 | #endif 256 | 257 | #if !defined(JSON_IS_AMALGAMATION) 258 | 259 | # include "version.h" 260 | 261 | # if JSONCPP_USING_SECURE_MEMORY 262 | # include "allocator.h" //typedef Allocator 263 | # endif 264 | 265 | #endif // if !defined(JSON_IS_AMALGAMATION) 266 | 267 | namespace Json { 268 | typedef int Int; 269 | typedef unsigned int UInt; 270 | #if defined(JSON_NO_INT64) 271 | typedef int LargestInt; 272 | typedef unsigned int LargestUInt; 273 | #undef JSON_HAS_INT64 274 | #else // if defined(JSON_NO_INT64) 275 | // For Microsoft Visual use specific types as long long is not supported 276 | #if defined(_MSC_VER) // Microsoft Visual Studio 277 | typedef __int64 Int64; 278 | typedef unsigned __int64 UInt64; 279 | #else // if defined(_MSC_VER) // Other platforms, use long long 280 | typedef int64_t Int64; 281 | typedef uint64_t UInt64; 282 | #endif // if defined(_MSC_VER) 283 | typedef Int64 LargestInt; 284 | typedef UInt64 LargestUInt; 285 | #define JSON_HAS_INT64 286 | #endif // if defined(JSON_NO_INT64) 287 | #if JSONCPP_USING_SECURE_MEMORY 288 | #define JSONCPP_STRING std::basic_string, Json::SecureAllocator > 289 | #define JSONCPP_OSTRINGSTREAM std::basic_ostringstream, Json::SecureAllocator > 290 | #define JSONCPP_OSTREAM std::basic_ostream> 291 | #define JSONCPP_ISTRINGSTREAM std::basic_istringstream, Json::SecureAllocator > 292 | #define JSONCPP_ISTREAM std::istream 293 | #else 294 | #define JSONCPP_STRING std::string 295 | #define JSONCPP_OSTRINGSTREAM std::ostringstream 296 | #define JSONCPP_OSTREAM std::ostream 297 | #define JSONCPP_ISTRINGSTREAM std::istringstream 298 | #define JSONCPP_ISTREAM std::istream 299 | #endif // if JSONCPP_USING_SECURE_MEMORY 300 | } // end namespace Json 301 | 302 | #endif // JSON_CONFIG_H_INCLUDED 303 | 304 | // ////////////////////////////////////////////////////////////////////// 305 | // End of content of file: include/json/config.h 306 | // ////////////////////////////////////////////////////////////////////// 307 | 308 | 309 | 310 | 311 | 312 | 313 | // ////////////////////////////////////////////////////////////////////// 314 | // Beginning of content of file: include/json/forwards.h 315 | // ////////////////////////////////////////////////////////////////////// 316 | 317 | // Copyright 2007-2010 Baptiste Lepilleur 318 | // Distributed under MIT license, or public domain if desired and 319 | // recognized in your jurisdiction. 320 | // See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE 321 | 322 | #ifndef JSON_FORWARDS_H_INCLUDED 323 | #define JSON_FORWARDS_H_INCLUDED 324 | 325 | #if !defined(JSON_IS_AMALGAMATION) 326 | #include "config.h" 327 | #endif // if !defined(JSON_IS_AMALGAMATION) 328 | 329 | namespace Json { 330 | 331 | // writer.h 332 | class FastWriter; 333 | class StyledWriter; 334 | 335 | // reader.h 336 | class Reader; 337 | 338 | // features.h 339 | class Features; 340 | 341 | // value.h 342 | typedef unsigned int ArrayIndex; 343 | class StaticString; 344 | class Path; 345 | class PathArgument; 346 | class Value; 347 | class ValueIteratorBase; 348 | class ValueIterator; 349 | class ValueConstIterator; 350 | 351 | } // namespace Json 352 | 353 | #endif // JSON_FORWARDS_H_INCLUDED 354 | 355 | // ////////////////////////////////////////////////////////////////////// 356 | // End of content of file: include/json/forwards.h 357 | // ////////////////////////////////////////////////////////////////////// 358 | 359 | 360 | 361 | 362 | 363 | 364 | // ////////////////////////////////////////////////////////////////////// 365 | // Beginning of content of file: include/json/features.h 366 | // ////////////////////////////////////////////////////////////////////// 367 | 368 | // Copyright 2007-2010 Baptiste Lepilleur 369 | // Distributed under MIT license, or public domain if desired and 370 | // recognized in your jurisdiction. 371 | // See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE 372 | 373 | #ifndef CPPTL_JSON_FEATURES_H_INCLUDED 374 | #define CPPTL_JSON_FEATURES_H_INCLUDED 375 | 376 | #if !defined(JSON_IS_AMALGAMATION) 377 | #include "forwards.h" 378 | #endif // if !defined(JSON_IS_AMALGAMATION) 379 | 380 | #pragma pack(push, 8) 381 | 382 | namespace Json { 383 | 384 | /** \brief Configuration passed to reader and writer. 385 | * This configuration object can be used to force the Reader or Writer 386 | * to behave in a standard conforming way. 387 | */ 388 | class JSON_API Features { 389 | public: 390 | /** \brief A configuration that allows all features and assumes all strings 391 | * are UTF-8. 392 | * - C & C++ comments are allowed 393 | * - Root object can be any JSON value 394 | * - Assumes Value strings are encoded in UTF-8 395 | */ 396 | static Features all(); 397 | 398 | /** \brief A configuration that is strictly compatible with the JSON 399 | * specification. 400 | * - Comments are forbidden. 401 | * - Root object must be either an array or an object value. 402 | * - Assumes Value strings are encoded in UTF-8 403 | */ 404 | static Features strictMode(); 405 | 406 | /** \brief Initialize the configuration like JsonConfig::allFeatures; 407 | */ 408 | Features(); 409 | 410 | /// \c true if comments are allowed. Default: \c true. 411 | bool allowComments_; 412 | 413 | /// \c true if root must be either an array or an object value. Default: \c 414 | /// false. 415 | bool strictRoot_; 416 | 417 | /// \c true if dropped null placeholders are allowed. Default: \c false. 418 | bool allowDroppedNullPlaceholders_; 419 | 420 | /// \c true if numeric object key are allowed. Default: \c false. 421 | bool allowNumericKeys_; 422 | }; 423 | 424 | } // namespace Json 425 | 426 | #pragma pack(pop) 427 | 428 | #endif // CPPTL_JSON_FEATURES_H_INCLUDED 429 | 430 | // ////////////////////////////////////////////////////////////////////// 431 | // End of content of file: include/json/features.h 432 | // ////////////////////////////////////////////////////////////////////// 433 | 434 | 435 | 436 | 437 | 438 | 439 | // ////////////////////////////////////////////////////////////////////// 440 | // Beginning of content of file: include/json/value.h 441 | // ////////////////////////////////////////////////////////////////////// 442 | 443 | // Copyright 2007-2010 Baptiste Lepilleur 444 | // Distributed under MIT license, or public domain if desired and 445 | // recognized in your jurisdiction. 446 | // See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE 447 | 448 | #ifndef CPPTL_JSON_H_INCLUDED 449 | #define CPPTL_JSON_H_INCLUDED 450 | 451 | #if !defined(JSON_IS_AMALGAMATION) 452 | #include "forwards.h" 453 | #endif // if !defined(JSON_IS_AMALGAMATION) 454 | #include 455 | #include 456 | #include 457 | 458 | #ifndef JSON_USE_CPPTL_SMALLMAP 459 | #include 460 | #else 461 | #include 462 | #endif 463 | #ifdef JSON_USE_CPPTL 464 | #include 465 | #endif 466 | 467 | //Conditional NORETURN attribute on the throw functions would: 468 | // a) suppress false positives from static code analysis 469 | // b) possibly improve optimization opportunities. 470 | #if !defined(JSONCPP_NORETURN) 471 | # if defined(_MSC_VER) 472 | # define JSONCPP_NORETURN __declspec(noreturn) 473 | # elif defined(__GNUC__) 474 | # define JSONCPP_NORETURN __attribute__ ((__noreturn__)) 475 | # else 476 | # define JSONCPP_NORETURN 477 | # endif 478 | #endif 479 | 480 | // Disable warning C4251: : needs to have dll-interface to 481 | // be used by... 482 | #if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) 483 | #pragma warning(push) 484 | #pragma warning(disable : 4251) 485 | #endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) 486 | 487 | #pragma pack(push, 8) 488 | 489 | /** \brief JSON (JavaScript Object Notation). 490 | */ 491 | namespace Json { 492 | 493 | /** Base class for all exceptions we throw. 494 | * 495 | * We use nothing but these internally. Of course, STL can throw others. 496 | */ 497 | class JSON_API Exception : public std::exception { 498 | public: 499 | Exception(JSONCPP_STRING const& msg); 500 | ~Exception() JSONCPP_NOEXCEPT JSONCPP_OVERRIDE; 501 | char const* what() const JSONCPP_NOEXCEPT JSONCPP_OVERRIDE; 502 | protected: 503 | JSONCPP_STRING msg_; 504 | }; 505 | 506 | /** Exceptions which the user cannot easily avoid. 507 | * 508 | * E.g. out-of-memory (when we use malloc), stack-overflow, malicious input 509 | * 510 | * \remark derived from Json::Exception 511 | */ 512 | class JSON_API RuntimeError : public Exception { 513 | public: 514 | RuntimeError(JSONCPP_STRING const& msg); 515 | }; 516 | 517 | /** Exceptions thrown by JSON_ASSERT/JSON_FAIL macros. 518 | * 519 | * These are precondition-violations (user bugs) and internal errors (our bugs). 520 | * 521 | * \remark derived from Json::Exception 522 | */ 523 | class JSON_API LogicError : public Exception { 524 | public: 525 | LogicError(JSONCPP_STRING const& msg); 526 | }; 527 | 528 | /// used internally 529 | JSONCPP_NORETURN void throwRuntimeError(JSONCPP_STRING const& msg); 530 | /// used internally 531 | JSONCPP_NORETURN void throwLogicError(JSONCPP_STRING const& msg); 532 | 533 | /** \brief Type of the value held by a Value object. 534 | */ 535 | enum ValueType { 536 | nullValue = 0, ///< 'null' value 537 | intValue, ///< signed integer value 538 | uintValue, ///< unsigned integer value 539 | realValue, ///< double value 540 | stringValue, ///< UTF-8 string value 541 | booleanValue, ///< bool value 542 | arrayValue, ///< array value (ordered list) 543 | objectValue ///< object value (collection of name/value pairs). 544 | }; 545 | 546 | enum CommentPlacement { 547 | commentBefore = 0, ///< a comment placed on the line before a value 548 | commentAfterOnSameLine, ///< a comment just after a value on the same line 549 | commentAfter, ///< a comment on the line after a value (only make sense for 550 | /// root value) 551 | numberOfCommentPlacement 552 | }; 553 | 554 | //# ifdef JSON_USE_CPPTL 555 | // typedef CppTL::AnyEnumerator EnumMemberNames; 556 | // typedef CppTL::AnyEnumerator EnumValues; 557 | //# endif 558 | 559 | /** \brief Lightweight wrapper to tag static string. 560 | * 561 | * Value constructor and objectValue member assignement takes advantage of the 562 | * StaticString and avoid the cost of string duplication when storing the 563 | * string or the member name. 564 | * 565 | * Example of usage: 566 | * \code 567 | * Json::Value aValue( StaticString("some text") ); 568 | * Json::Value object; 569 | * static const StaticString code("code"); 570 | * object[code] = 1234; 571 | * \endcode 572 | */ 573 | class JSON_API StaticString { 574 | public: 575 | explicit StaticString(const char* czstring) : c_str_(czstring) {} 576 | 577 | operator const char*() const { return c_str_; } 578 | 579 | const char* c_str() const { return c_str_; } 580 | 581 | private: 582 | const char* c_str_; 583 | }; 584 | 585 | /** \brief Represents a JSON value. 586 | * 587 | * This class is a discriminated union wrapper that can represents a: 588 | * - signed integer [range: Value::minInt - Value::maxInt] 589 | * - unsigned integer (range: 0 - Value::maxUInt) 590 | * - double 591 | * - UTF-8 string 592 | * - boolean 593 | * - 'null' 594 | * - an ordered list of Value 595 | * - collection of name/value pairs (javascript object) 596 | * 597 | * The type of the held value is represented by a #ValueType and 598 | * can be obtained using type(). 599 | * 600 | * Values of an #objectValue or #arrayValue can be accessed using operator[]() 601 | * methods. 602 | * Non-const methods will automatically create the a #nullValue element 603 | * if it does not exist. 604 | * The sequence of an #arrayValue will be automatically resized and initialized 605 | * with #nullValue. resize() can be used to enlarge or truncate an #arrayValue. 606 | * 607 | * The get() methods can be used to obtain default value in the case the 608 | * required element does not exist. 609 | * 610 | * It is possible to iterate over the list of a #objectValue values using 611 | * the getMemberNames() method. 612 | * 613 | * \note #Value string-length fit in size_t, but keys must be < 2^30. 614 | * (The reason is an implementation detail.) A #CharReader will raise an 615 | * exception if a bound is exceeded to avoid security holes in your app, 616 | * but the Value API does *not* check bounds. That is the responsibility 617 | * of the caller. 618 | */ 619 | class JSON_API Value { 620 | friend class ValueIteratorBase; 621 | public: 622 | typedef std::vector Members; 623 | typedef ValueIterator iterator; 624 | typedef ValueConstIterator const_iterator; 625 | typedef Json::UInt UInt; 626 | typedef Json::Int Int; 627 | #if defined(JSON_HAS_INT64) 628 | typedef Json::UInt64 UInt64; 629 | typedef Json::Int64 Int64; 630 | #endif // defined(JSON_HAS_INT64) 631 | typedef Json::LargestInt LargestInt; 632 | typedef Json::LargestUInt LargestUInt; 633 | typedef Json::ArrayIndex ArrayIndex; 634 | 635 | static const Value& null; ///< We regret this reference to a global instance; prefer the simpler Value(). 636 | static const Value& nullRef; ///< just a kludge for binary-compatibility; same as null 637 | static Value const& nullSingleton(); ///< Prefer this to null or nullRef. 638 | 639 | /// Minimum signed integer value that can be stored in a Json::Value. 640 | static const LargestInt minLargestInt; 641 | /// Maximum signed integer value that can be stored in a Json::Value. 642 | static const LargestInt maxLargestInt; 643 | /// Maximum unsigned integer value that can be stored in a Json::Value. 644 | static const LargestUInt maxLargestUInt; 645 | 646 | /// Minimum signed int value that can be stored in a Json::Value. 647 | static const Int minInt; 648 | /// Maximum signed int value that can be stored in a Json::Value. 649 | static const Int maxInt; 650 | /// Maximum unsigned int value that can be stored in a Json::Value. 651 | static const UInt maxUInt; 652 | 653 | #if defined(JSON_HAS_INT64) 654 | /// Minimum signed 64 bits int value that can be stored in a Json::Value. 655 | static const Int64 minInt64; 656 | /// Maximum signed 64 bits int value that can be stored in a Json::Value. 657 | static const Int64 maxInt64; 658 | /// Maximum unsigned 64 bits int value that can be stored in a Json::Value. 659 | static const UInt64 maxUInt64; 660 | #endif // defined(JSON_HAS_INT64) 661 | 662 | private: 663 | #ifndef JSONCPP_DOC_EXCLUDE_IMPLEMENTATION 664 | class CZString { 665 | public: 666 | enum DuplicationPolicy { 667 | noDuplication = 0, 668 | duplicate, 669 | duplicateOnCopy 670 | }; 671 | CZString(ArrayIndex index); 672 | CZString(char const* str, unsigned length, DuplicationPolicy allocate); 673 | CZString(CZString const& other); 674 | #if JSON_HAS_RVALUE_REFERENCES 675 | CZString(CZString&& other); 676 | #endif 677 | ~CZString(); 678 | CZString& operator=(CZString other); 679 | bool operator<(CZString const& other) const; 680 | bool operator==(CZString const& other) const; 681 | ArrayIndex index() const; 682 | //const char* c_str() const; ///< \deprecated 683 | char const* data() const; 684 | unsigned length() const; 685 | bool isStaticString() const; 686 | 687 | private: 688 | void swap(CZString& other); 689 | 690 | struct StringStorage { 691 | unsigned policy_: 2; 692 | unsigned length_: 30; // 1GB max 693 | }; 694 | 695 | char const* cstr_; // actually, a prefixed string, unless policy is noDup 696 | union { 697 | ArrayIndex index_; 698 | StringStorage storage_; 699 | }; 700 | }; 701 | 702 | public: 703 | #ifndef JSON_USE_CPPTL_SMALLMAP 704 | typedef std::map ObjectValues; 705 | #else 706 | typedef CppTL::SmallMap ObjectValues; 707 | #endif // ifndef JSON_USE_CPPTL_SMALLMAP 708 | #endif // ifndef JSONCPP_DOC_EXCLUDE_IMPLEMENTATION 709 | 710 | public: 711 | /** \brief Create a default Value of the given type. 712 | 713 | This is a very useful constructor. 714 | To create an empty array, pass arrayValue. 715 | To create an empty object, pass objectValue. 716 | Another Value can then be set to this one by assignment. 717 | This is useful since clear() and resize() will not alter types. 718 | 719 | Examples: 720 | \code 721 | Json::Value null_value; // null 722 | Json::Value arr_value(Json::arrayValue); // [] 723 | Json::Value obj_value(Json::objectValue); // {} 724 | \endcode 725 | */ 726 | Value(ValueType type = nullValue); 727 | Value(Int value); 728 | Value(UInt value); 729 | #if defined(JSON_HAS_INT64) 730 | Value(Int64 value); 731 | Value(UInt64 value); 732 | #endif // if defined(JSON_HAS_INT64) 733 | Value(double value); 734 | Value(const char* value); ///< Copy til first 0. (NULL causes to seg-fault.) 735 | Value(const char* begin, const char* end); ///< Copy all, incl zeroes. 736 | /** \brief Constructs a value from a static string. 737 | 738 | * Like other value string constructor but do not duplicate the string for 739 | * internal storage. The given string must remain alive after the call to this 740 | * constructor. 741 | * \note This works only for null-terminated strings. (We cannot change the 742 | * size of this class, so we have nowhere to store the length, 743 | * which might be computed later for various operations.) 744 | * 745 | * Example of usage: 746 | * \code 747 | * static StaticString foo("some text"); 748 | * Json::Value aValue(foo); 749 | * \endcode 750 | */ 751 | Value(const StaticString& value); 752 | Value(const JSONCPP_STRING& value); ///< Copy data() til size(). Embedded zeroes too. 753 | #ifdef JSON_USE_CPPTL 754 | Value(const CppTL::ConstString& value); 755 | #endif 756 | Value(bool value); 757 | /// Deep copy. 758 | Value(const Value& other); 759 | #if JSON_HAS_RVALUE_REFERENCES 760 | /// Move constructor 761 | Value(Value&& other); 762 | #endif 763 | ~Value(); 764 | 765 | /// Deep copy, then swap(other). 766 | /// \note Over-write existing comments. To preserve comments, use #swapPayload(). 767 | Value& operator=(Value other); 768 | /// Swap everything. 769 | void swap(Value& other); 770 | /// Swap values but leave comments and source offsets in place. 771 | void swapPayload(Value& other); 772 | 773 | ValueType type() const; 774 | 775 | /// Compare payload only, not comments etc. 776 | bool operator<(const Value& other) const; 777 | bool operator<=(const Value& other) const; 778 | bool operator>=(const Value& other) const; 779 | bool operator>(const Value& other) const; 780 | bool operator==(const Value& other) const; 781 | bool operator!=(const Value& other) const; 782 | int compare(const Value& other) const; 783 | 784 | const char* asCString() const; ///< Embedded zeroes could cause you trouble! 785 | #if JSONCPP_USING_SECURE_MEMORY 786 | unsigned getCStringLength() const; //Allows you to understand the length of the CString 787 | #endif 788 | JSONCPP_STRING asString() const; ///< Embedded zeroes are possible. 789 | /** Get raw char* of string-value. 790 | * \return false if !string. (Seg-fault if str or end are NULL.) 791 | */ 792 | bool getString( 793 | char const** begin, char const** end) const; 794 | #ifdef JSON_USE_CPPTL 795 | CppTL::ConstString asConstString() const; 796 | #endif 797 | Int asInt() const; 798 | UInt asUInt() const; 799 | #if defined(JSON_HAS_INT64) 800 | Int64 asInt64() const; 801 | UInt64 asUInt64() const; 802 | #endif // if defined(JSON_HAS_INT64) 803 | LargestInt asLargestInt() const; 804 | LargestUInt asLargestUInt() const; 805 | float asFloat() const; 806 | double asDouble() const; 807 | bool asBool() const; 808 | 809 | bool isNull() const; 810 | bool isBool() const; 811 | bool isInt() const; 812 | bool isInt64() const; 813 | bool isUInt() const; 814 | bool isUInt64() const; 815 | bool isIntegral() const; 816 | bool isDouble() const; 817 | bool isNumeric() const; 818 | bool isString() const; 819 | bool isArray() const; 820 | bool isObject() const; 821 | 822 | bool isConvertibleTo(ValueType other) const; 823 | 824 | /// Number of values in array or object 825 | ArrayIndex size() const; 826 | 827 | /// \brief Return true if empty array, empty object, or null; 828 | /// otherwise, false. 829 | bool empty() const; 830 | 831 | /// Return isNull() 832 | bool operator!() const; 833 | 834 | /// Remove all object members and array elements. 835 | /// \pre type() is arrayValue, objectValue, or nullValue 836 | /// \post type() is unchanged 837 | void clear(); 838 | 839 | /// Resize the array to size elements. 840 | /// New elements are initialized to null. 841 | /// May only be called on nullValue or arrayValue. 842 | /// \pre type() is arrayValue or nullValue 843 | /// \post type() is arrayValue 844 | void resize(ArrayIndex size); 845 | 846 | /// Access an array element (zero based index ). 847 | /// If the array contains less than index element, then null value are 848 | /// inserted 849 | /// in the array so that its size is index+1. 850 | /// (You may need to say 'value[0u]' to get your compiler to distinguish 851 | /// this from the operator[] which takes a string.) 852 | Value& operator[](ArrayIndex index); 853 | 854 | /// Access an array element (zero based index ). 855 | /// If the array contains less than index element, then null value are 856 | /// inserted 857 | /// in the array so that its size is index+1. 858 | /// (You may need to say 'value[0u]' to get your compiler to distinguish 859 | /// this from the operator[] which takes a string.) 860 | Value& operator[](int index); 861 | 862 | /// Access an array element (zero based index ) 863 | /// (You may need to say 'value[0u]' to get your compiler to distinguish 864 | /// this from the operator[] which takes a string.) 865 | const Value& operator[](ArrayIndex index) const; 866 | 867 | /// Access an array element (zero based index ) 868 | /// (You may need to say 'value[0u]' to get your compiler to distinguish 869 | /// this from the operator[] which takes a string.) 870 | const Value& operator[](int index) const; 871 | 872 | /// If the array contains at least index+1 elements, returns the element 873 | /// value, 874 | /// otherwise returns defaultValue. 875 | Value get(ArrayIndex index, const Value& defaultValue) const; 876 | /// Return true if index < size(). 877 | bool isValidIndex(ArrayIndex index) const; 878 | /// \brief Append value to array at the end. 879 | /// 880 | /// Equivalent to jsonvalue[jsonvalue.size()] = value; 881 | Value& append(const Value& value); 882 | 883 | /// Access an object value by name, create a null member if it does not exist. 884 | /// \note Because of our implementation, keys are limited to 2^30 -1 chars. 885 | /// Exceeding that will cause an exception. 886 | Value& operator[](const char* key); 887 | /// Access an object value by name, returns null if there is no member with 888 | /// that name. 889 | const Value& operator[](const char* key) const; 890 | /// Access an object value by name, create a null member if it does not exist. 891 | /// \param key may contain embedded nulls. 892 | Value& operator[](const JSONCPP_STRING& key); 893 | /// Access an object value by name, returns null if there is no member with 894 | /// that name. 895 | /// \param key may contain embedded nulls. 896 | const Value& operator[](const JSONCPP_STRING& key) const; 897 | /** \brief Access an object value by name, create a null member if it does not 898 | exist. 899 | 900 | * If the object has no entry for that name, then the member name used to store 901 | * the new entry is not duplicated. 902 | * Example of use: 903 | * \code 904 | * Json::Value object; 905 | * static const StaticString code("code"); 906 | * object[code] = 1234; 907 | * \endcode 908 | */ 909 | Value& operator[](const StaticString& key); 910 | #ifdef JSON_USE_CPPTL 911 | /// Access an object value by name, create a null member if it does not exist. 912 | Value& operator[](const CppTL::ConstString& key); 913 | /// Access an object value by name, returns null if there is no member with 914 | /// that name. 915 | const Value& operator[](const CppTL::ConstString& key) const; 916 | #endif 917 | /// Return the member named key if it exist, defaultValue otherwise. 918 | /// \note deep copy 919 | Value get(const char* key, const Value& defaultValue) const; 920 | /// Return the member named key if it exist, defaultValue otherwise. 921 | /// \note deep copy 922 | /// \note key may contain embedded nulls. 923 | Value get(const char* begin, const char* end, const Value& defaultValue) const; 924 | /// Return the member named key if it exist, defaultValue otherwise. 925 | /// \note deep copy 926 | /// \param key may contain embedded nulls. 927 | Value get(const JSONCPP_STRING& key, const Value& defaultValue) const; 928 | #ifdef JSON_USE_CPPTL 929 | /// Return the member named key if it exist, defaultValue otherwise. 930 | /// \note deep copy 931 | Value get(const CppTL::ConstString& key, const Value& defaultValue) const; 932 | #endif 933 | /// Most general and efficient version of isMember()const, get()const, 934 | /// and operator[]const 935 | /// \note As stated elsewhere, behavior is undefined if (end-begin) >= 2^30 936 | Value const* find(char const* begin, char const* end) const; 937 | /// Most general and efficient version of object-mutators. 938 | /// \note As stated elsewhere, behavior is undefined if (end-begin) >= 2^30 939 | /// \return non-zero, but JSON_ASSERT if this is neither object nor nullValue. 940 | Value const* demand(char const* begin, char const* end); 941 | /// \brief Remove and return the named member. 942 | /// 943 | /// Do nothing if it did not exist. 944 | /// \return the removed Value, or null. 945 | /// \pre type() is objectValue or nullValue 946 | /// \post type() is unchanged 947 | /// \deprecated 948 | Value removeMember(const char* key); 949 | /// Same as removeMember(const char*) 950 | /// \param key may contain embedded nulls. 951 | /// \deprecated 952 | Value removeMember(const JSONCPP_STRING& key); 953 | /// Same as removeMember(const char* begin, const char* end, Value* removed), 954 | /// but 'key' is null-terminated. 955 | bool removeMember(const char* key, Value* removed); 956 | /** \brief Remove the named map member. 957 | 958 | Update 'removed' iff removed. 959 | \param key may contain embedded nulls. 960 | \return true iff removed (no exceptions) 961 | */ 962 | bool removeMember(JSONCPP_STRING const& key, Value* removed); 963 | /// Same as removeMember(JSONCPP_STRING const& key, Value* removed) 964 | bool removeMember(const char* begin, const char* end, Value* removed); 965 | /** \brief Remove the indexed array element. 966 | 967 | O(n) expensive operations. 968 | Update 'removed' iff removed. 969 | \return true iff removed (no exceptions) 970 | */ 971 | bool removeIndex(ArrayIndex i, Value* removed); 972 | 973 | /// Return true if the object has a member named key. 974 | /// \note 'key' must be null-terminated. 975 | bool isMember(const char* key) const; 976 | /// Return true if the object has a member named key. 977 | /// \param key may contain embedded nulls. 978 | bool isMember(const JSONCPP_STRING& key) const; 979 | /// Same as isMember(JSONCPP_STRING const& key)const 980 | bool isMember(const char* begin, const char* end) const; 981 | #ifdef JSON_USE_CPPTL 982 | /// Return true if the object has a member named key. 983 | bool isMember(const CppTL::ConstString& key) const; 984 | #endif 985 | 986 | /// \brief Return a list of the member names. 987 | /// 988 | /// If null, return an empty list. 989 | /// \pre type() is objectValue or nullValue 990 | /// \post if type() was nullValue, it remains nullValue 991 | Members getMemberNames() const; 992 | 993 | //# ifdef JSON_USE_CPPTL 994 | // EnumMemberNames enumMemberNames() const; 995 | // EnumValues enumValues() const; 996 | //# endif 997 | 998 | /// \deprecated Always pass len. 999 | JSONCPP_DEPRECATED("Use setComment(JSONCPP_STRING const&) instead.") 1000 | void setComment(const char* comment, CommentPlacement placement); 1001 | /// Comments must be //... or /* ... */ 1002 | void setComment(const char* comment, size_t len, CommentPlacement placement); 1003 | /// Comments must be //... or /* ... */ 1004 | void setComment(const JSONCPP_STRING& comment, CommentPlacement placement); 1005 | bool hasComment(CommentPlacement placement) const; 1006 | /// Include delimiters and embedded newlines. 1007 | JSONCPP_STRING getComment(CommentPlacement placement) const; 1008 | 1009 | JSONCPP_STRING toStyledString() const; 1010 | 1011 | const_iterator begin() const; 1012 | const_iterator end() const; 1013 | 1014 | iterator begin(); 1015 | iterator end(); 1016 | 1017 | // Accessors for the [start, limit) range of bytes within the JSON text from 1018 | // which this value was parsed, if any. 1019 | void setOffsetStart(ptrdiff_t start); 1020 | void setOffsetLimit(ptrdiff_t limit); 1021 | ptrdiff_t getOffsetStart() const; 1022 | ptrdiff_t getOffsetLimit() const; 1023 | 1024 | private: 1025 | void initBasic(ValueType type, bool allocated = false); 1026 | 1027 | Value& resolveReference(const char* key); 1028 | Value& resolveReference(const char* key, const char* end); 1029 | 1030 | struct CommentInfo { 1031 | CommentInfo(); 1032 | ~CommentInfo(); 1033 | 1034 | void setComment(const char* text, size_t len); 1035 | 1036 | char* comment_; 1037 | }; 1038 | 1039 | // struct MemberNamesTransform 1040 | //{ 1041 | // typedef const char *result_type; 1042 | // const char *operator()( const CZString &name ) const 1043 | // { 1044 | // return name.c_str(); 1045 | // } 1046 | //}; 1047 | 1048 | union ValueHolder { 1049 | LargestInt int_; 1050 | LargestUInt uint_; 1051 | double real_; 1052 | bool bool_; 1053 | char* string_; // actually ptr to unsigned, followed by str, unless !allocated_ 1054 | ObjectValues* map_; 1055 | } value_; 1056 | ValueType type_ : 8; 1057 | unsigned int allocated_ : 1; // Notes: if declared as bool, bitfield is useless. 1058 | // If not allocated_, string_ must be null-terminated. 1059 | CommentInfo* comments_; 1060 | 1061 | // [start, limit) byte offsets in the source JSON text from which this Value 1062 | // was extracted. 1063 | ptrdiff_t start_; 1064 | ptrdiff_t limit_; 1065 | }; 1066 | 1067 | /** \brief Experimental and untested: represents an element of the "path" to 1068 | * access a node. 1069 | */ 1070 | class JSON_API PathArgument { 1071 | public: 1072 | friend class Path; 1073 | 1074 | PathArgument(); 1075 | PathArgument(ArrayIndex index); 1076 | PathArgument(const char* key); 1077 | PathArgument(const JSONCPP_STRING& key); 1078 | 1079 | private: 1080 | enum Kind { 1081 | kindNone = 0, 1082 | kindIndex, 1083 | kindKey 1084 | }; 1085 | JSONCPP_STRING key_; 1086 | ArrayIndex index_; 1087 | Kind kind_; 1088 | }; 1089 | 1090 | /** \brief Experimental and untested: represents a "path" to access a node. 1091 | * 1092 | * Syntax: 1093 | * - "." => root node 1094 | * - ".[n]" => elements at index 'n' of root node (an array value) 1095 | * - ".name" => member named 'name' of root node (an object value) 1096 | * - ".name1.name2.name3" 1097 | * - ".[0][1][2].name1[3]" 1098 | * - ".%" => member name is provided as parameter 1099 | * - ".[%]" => index is provied as parameter 1100 | */ 1101 | class JSON_API Path { 1102 | public: 1103 | Path(const JSONCPP_STRING& path, 1104 | const PathArgument& a1 = PathArgument(), 1105 | const PathArgument& a2 = PathArgument(), 1106 | const PathArgument& a3 = PathArgument(), 1107 | const PathArgument& a4 = PathArgument(), 1108 | const PathArgument& a5 = PathArgument()); 1109 | 1110 | const Value& resolve(const Value& root) const; 1111 | Value resolve(const Value& root, const Value& defaultValue) const; 1112 | /// Creates the "path" to access the specified node and returns a reference on 1113 | /// the node. 1114 | Value& make(Value& root) const; 1115 | 1116 | private: 1117 | typedef std::vector InArgs; 1118 | typedef std::vector Args; 1119 | 1120 | void makePath(const JSONCPP_STRING& path, const InArgs& in); 1121 | void addPathInArg(const JSONCPP_STRING& path, 1122 | const InArgs& in, 1123 | InArgs::const_iterator& itInArg, 1124 | PathArgument::Kind kind); 1125 | void invalidPath(const JSONCPP_STRING& path, int location); 1126 | 1127 | Args args_; 1128 | }; 1129 | 1130 | /** \brief base class for Value iterators. 1131 | * 1132 | */ 1133 | class JSON_API ValueIteratorBase { 1134 | public: 1135 | typedef std::bidirectional_iterator_tag iterator_category; 1136 | typedef unsigned int size_t; 1137 | typedef int difference_type; 1138 | typedef ValueIteratorBase SelfType; 1139 | 1140 | bool operator==(const SelfType& other) const { return isEqual(other); } 1141 | 1142 | bool operator!=(const SelfType& other) const { return !isEqual(other); } 1143 | 1144 | difference_type operator-(const SelfType& other) const { 1145 | return other.computeDistance(*this); 1146 | } 1147 | 1148 | /// Return either the index or the member name of the referenced value as a 1149 | /// Value. 1150 | Value key() const; 1151 | 1152 | /// Return the index of the referenced Value, or -1 if it is not an arrayValue. 1153 | UInt index() const; 1154 | 1155 | /// Return the member name of the referenced Value, or "" if it is not an 1156 | /// objectValue. 1157 | /// \note Avoid `c_str()` on result, as embedded zeroes are possible. 1158 | JSONCPP_STRING name() const; 1159 | 1160 | /// Return the member name of the referenced Value. "" if it is not an 1161 | /// objectValue. 1162 | /// \deprecated This cannot be used for UTF-8 strings, since there can be embedded nulls. 1163 | JSONCPP_DEPRECATED("Use `key = name();` instead.") 1164 | char const* memberName() const; 1165 | /// Return the member name of the referenced Value, or NULL if it is not an 1166 | /// objectValue. 1167 | /// \note Better version than memberName(). Allows embedded nulls. 1168 | char const* memberName(char const** end) const; 1169 | 1170 | protected: 1171 | Value& deref() const; 1172 | 1173 | void increment(); 1174 | 1175 | void decrement(); 1176 | 1177 | difference_type computeDistance(const SelfType& other) const; 1178 | 1179 | bool isEqual(const SelfType& other) const; 1180 | 1181 | void copy(const SelfType& other); 1182 | 1183 | private: 1184 | Value::ObjectValues::iterator current_; 1185 | // Indicates that iterator is for a null value. 1186 | bool isNull_; 1187 | 1188 | public: 1189 | // For some reason, BORLAND needs these at the end, rather 1190 | // than earlier. No idea why. 1191 | ValueIteratorBase(); 1192 | explicit ValueIteratorBase(const Value::ObjectValues::iterator& current); 1193 | }; 1194 | 1195 | /** \brief const iterator for object and array value. 1196 | * 1197 | */ 1198 | class JSON_API ValueConstIterator : public ValueIteratorBase { 1199 | friend class Value; 1200 | 1201 | public: 1202 | typedef const Value value_type; 1203 | //typedef unsigned int size_t; 1204 | //typedef int difference_type; 1205 | typedef const Value& reference; 1206 | typedef const Value* pointer; 1207 | typedef ValueConstIterator SelfType; 1208 | 1209 | ValueConstIterator(); 1210 | ValueConstIterator(ValueIterator const& other); 1211 | 1212 | private: 1213 | /*! \internal Use by Value to create an iterator. 1214 | */ 1215 | explicit ValueConstIterator(const Value::ObjectValues::iterator& current); 1216 | public: 1217 | SelfType& operator=(const ValueIteratorBase& other); 1218 | 1219 | SelfType operator++(int) { 1220 | SelfType temp(*this); 1221 | ++*this; 1222 | return temp; 1223 | } 1224 | 1225 | SelfType operator--(int) { 1226 | SelfType temp(*this); 1227 | --*this; 1228 | return temp; 1229 | } 1230 | 1231 | SelfType& operator--() { 1232 | decrement(); 1233 | return *this; 1234 | } 1235 | 1236 | SelfType& operator++() { 1237 | increment(); 1238 | return *this; 1239 | } 1240 | 1241 | reference operator*() const { return deref(); } 1242 | 1243 | pointer operator->() const { return &deref(); } 1244 | }; 1245 | 1246 | /** \brief Iterator for object and array value. 1247 | */ 1248 | class JSON_API ValueIterator : public ValueIteratorBase { 1249 | friend class Value; 1250 | 1251 | public: 1252 | typedef Value value_type; 1253 | typedef unsigned int size_t; 1254 | typedef int difference_type; 1255 | typedef Value& reference; 1256 | typedef Value* pointer; 1257 | typedef ValueIterator SelfType; 1258 | 1259 | ValueIterator(); 1260 | explicit ValueIterator(const ValueConstIterator& other); 1261 | ValueIterator(const ValueIterator& other); 1262 | 1263 | private: 1264 | /*! \internal Use by Value to create an iterator. 1265 | */ 1266 | explicit ValueIterator(const Value::ObjectValues::iterator& current); 1267 | public: 1268 | SelfType& operator=(const SelfType& other); 1269 | 1270 | SelfType operator++(int) { 1271 | SelfType temp(*this); 1272 | ++*this; 1273 | return temp; 1274 | } 1275 | 1276 | SelfType operator--(int) { 1277 | SelfType temp(*this); 1278 | --*this; 1279 | return temp; 1280 | } 1281 | 1282 | SelfType& operator--() { 1283 | decrement(); 1284 | return *this; 1285 | } 1286 | 1287 | SelfType& operator++() { 1288 | increment(); 1289 | return *this; 1290 | } 1291 | 1292 | reference operator*() const { return deref(); } 1293 | 1294 | pointer operator->() const { return &deref(); } 1295 | }; 1296 | 1297 | } // namespace Json 1298 | 1299 | 1300 | namespace std { 1301 | /// Specialize std::swap() for Json::Value. 1302 | template<> 1303 | inline void swap(Json::Value& a, Json::Value& b) { a.swap(b); } 1304 | } 1305 | 1306 | #pragma pack(pop) 1307 | 1308 | #if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) 1309 | #pragma warning(pop) 1310 | #endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) 1311 | 1312 | #endif // CPPTL_JSON_H_INCLUDED 1313 | 1314 | // ////////////////////////////////////////////////////////////////////// 1315 | // End of content of file: include/json/value.h 1316 | // ////////////////////////////////////////////////////////////////////// 1317 | 1318 | 1319 | 1320 | 1321 | 1322 | 1323 | // ////////////////////////////////////////////////////////////////////// 1324 | // Beginning of content of file: include/json/reader.h 1325 | // ////////////////////////////////////////////////////////////////////// 1326 | 1327 | // Copyright 2007-2010 Baptiste Lepilleur 1328 | // Distributed under MIT license, or public domain if desired and 1329 | // recognized in your jurisdiction. 1330 | // See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE 1331 | 1332 | #ifndef CPPTL_JSON_READER_H_INCLUDED 1333 | #define CPPTL_JSON_READER_H_INCLUDED 1334 | 1335 | #if !defined(JSON_IS_AMALGAMATION) 1336 | #include "features.h" 1337 | #include "value.h" 1338 | #endif // if !defined(JSON_IS_AMALGAMATION) 1339 | #include 1340 | #include 1341 | #include 1342 | #include 1343 | #include 1344 | 1345 | // Disable warning C4251: : needs to have dll-interface to 1346 | // be used by... 1347 | #if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) 1348 | #pragma warning(push) 1349 | #pragma warning(disable : 4251) 1350 | #endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) 1351 | 1352 | #pragma pack(push, 8) 1353 | 1354 | namespace Json { 1355 | 1356 | /** \brief Unserialize a JSON document into a 1357 | *Value. 1358 | * 1359 | * \deprecated Use CharReader and CharReaderBuilder. 1360 | */ 1361 | class JSON_API Reader { 1362 | public: 1363 | typedef char Char; 1364 | typedef const Char* Location; 1365 | 1366 | /** \brief An error tagged with where in the JSON text it was encountered. 1367 | * 1368 | * The offsets give the [start, limit) range of bytes within the text. Note 1369 | * that this is bytes, not codepoints. 1370 | * 1371 | */ 1372 | struct StructuredError { 1373 | ptrdiff_t offset_start; 1374 | ptrdiff_t offset_limit; 1375 | JSONCPP_STRING message; 1376 | }; 1377 | 1378 | /** \brief Constructs a Reader allowing all features 1379 | * for parsing. 1380 | */ 1381 | Reader(); 1382 | 1383 | /** \brief Constructs a Reader allowing the specified feature set 1384 | * for parsing. 1385 | */ 1386 | Reader(const Features& features); 1387 | 1388 | /** \brief Read a Value from a JSON 1389 | * document. 1390 | * \param document UTF-8 encoded string containing the document to read. 1391 | * \param root [out] Contains the root value of the document if it was 1392 | * successfully parsed. 1393 | * \param collectComments \c true to collect comment and allow writing them 1394 | * back during 1395 | * serialization, \c false to discard comments. 1396 | * This parameter is ignored if 1397 | * Features::allowComments_ 1398 | * is \c false. 1399 | * \return \c true if the document was successfully parsed, \c false if an 1400 | * error occurred. 1401 | */ 1402 | bool 1403 | parse(const std::string& document, Value& root, bool collectComments = true); 1404 | 1405 | /** \brief Read a Value from a JSON 1406 | document. 1407 | * \param beginDoc Pointer on the beginning of the UTF-8 encoded string of the 1408 | document to read. 1409 | * \param endDoc Pointer on the end of the UTF-8 encoded string of the 1410 | document to read. 1411 | * Must be >= beginDoc. 1412 | * \param root [out] Contains the root value of the document if it was 1413 | * successfully parsed. 1414 | * \param collectComments \c true to collect comment and allow writing them 1415 | back during 1416 | * serialization, \c false to discard comments. 1417 | * This parameter is ignored if 1418 | Features::allowComments_ 1419 | * is \c false. 1420 | * \return \c true if the document was successfully parsed, \c false if an 1421 | error occurred. 1422 | */ 1423 | bool parse(const char* beginDoc, 1424 | const char* endDoc, 1425 | Value& root, 1426 | bool collectComments = true); 1427 | 1428 | /// \brief Parse from input stream. 1429 | /// \see Json::operator>>(std::istream&, Json::Value&). 1430 | bool parse(JSONCPP_ISTREAM& is, Value& root, bool collectComments = true); 1431 | 1432 | /** \brief Returns a user friendly string that list errors in the parsed 1433 | * document. 1434 | * \return Formatted error message with the list of errors with their location 1435 | * in 1436 | * the parsed document. An empty string is returned if no error 1437 | * occurred 1438 | * during parsing. 1439 | * \deprecated Use getFormattedErrorMessages() instead (typo fix). 1440 | */ 1441 | JSONCPP_DEPRECATED("Use getFormattedErrorMessages() instead.") 1442 | JSONCPP_STRING getFormatedErrorMessages() const; 1443 | 1444 | /** \brief Returns a user friendly string that list errors in the parsed 1445 | * document. 1446 | * \return Formatted error message with the list of errors with their location 1447 | * in 1448 | * the parsed document. An empty string is returned if no error 1449 | * occurred 1450 | * during parsing. 1451 | */ 1452 | JSONCPP_STRING getFormattedErrorMessages() const; 1453 | 1454 | /** \brief Returns a vector of structured erros encounted while parsing. 1455 | * \return A (possibly empty) vector of StructuredError objects. Currently 1456 | * only one error can be returned, but the caller should tolerate 1457 | * multiple 1458 | * errors. This can occur if the parser recovers from a non-fatal 1459 | * parse error and then encounters additional errors. 1460 | */ 1461 | std::vector getStructuredErrors() const; 1462 | 1463 | /** \brief Add a semantic error message. 1464 | * \param value JSON Value location associated with the error 1465 | * \param message The error message. 1466 | * \return \c true if the error was successfully added, \c false if the 1467 | * Value offset exceeds the document size. 1468 | */ 1469 | bool pushError(const Value& value, const JSONCPP_STRING& message); 1470 | 1471 | /** \brief Add a semantic error message with extra context. 1472 | * \param value JSON Value location associated with the error 1473 | * \param message The error message. 1474 | * \param extra Additional JSON Value location to contextualize the error 1475 | * \return \c true if the error was successfully added, \c false if either 1476 | * Value offset exceeds the document size. 1477 | */ 1478 | bool pushError(const Value& value, const JSONCPP_STRING& message, const Value& extra); 1479 | 1480 | /** \brief Return whether there are any errors. 1481 | * \return \c true if there are no errors to report \c false if 1482 | * errors have occurred. 1483 | */ 1484 | bool good() const; 1485 | 1486 | private: 1487 | enum TokenType { 1488 | tokenEndOfStream = 0, 1489 | tokenObjectBegin, 1490 | tokenObjectEnd, 1491 | tokenArrayBegin, 1492 | tokenArrayEnd, 1493 | tokenString, 1494 | tokenNumber, 1495 | tokenTrue, 1496 | tokenFalse, 1497 | tokenNull, 1498 | tokenArraySeparator, 1499 | tokenMemberSeparator, 1500 | tokenComment, 1501 | tokenError 1502 | }; 1503 | 1504 | class Token { 1505 | public: 1506 | TokenType type_; 1507 | Location start_; 1508 | Location end_; 1509 | }; 1510 | 1511 | class ErrorInfo { 1512 | public: 1513 | Token token_; 1514 | JSONCPP_STRING message_; 1515 | Location extra_; 1516 | }; 1517 | 1518 | typedef std::deque Errors; 1519 | 1520 | bool readToken(Token& token); 1521 | void skipSpaces(); 1522 | bool match(Location pattern, int patternLength); 1523 | bool readComment(); 1524 | bool readCStyleComment(); 1525 | bool readCppStyleComment(); 1526 | bool readString(); 1527 | void readNumber(); 1528 | bool readValue(); 1529 | bool readObject(Token& token); 1530 | bool readArray(Token& token); 1531 | bool decodeNumber(Token& token); 1532 | bool decodeNumber(Token& token, Value& decoded); 1533 | bool decodeString(Token& token); 1534 | bool decodeString(Token& token, JSONCPP_STRING& decoded); 1535 | bool decodeDouble(Token& token); 1536 | bool decodeDouble(Token& token, Value& decoded); 1537 | bool decodeUnicodeCodePoint(Token& token, 1538 | Location& current, 1539 | Location end, 1540 | unsigned int& unicode); 1541 | bool decodeUnicodeEscapeSequence(Token& token, 1542 | Location& current, 1543 | Location end, 1544 | unsigned int& unicode); 1545 | bool addError(const JSONCPP_STRING& message, Token& token, Location extra = 0); 1546 | bool recoverFromError(TokenType skipUntilToken); 1547 | bool addErrorAndRecover(const JSONCPP_STRING& message, 1548 | Token& token, 1549 | TokenType skipUntilToken); 1550 | void skipUntilSpace(); 1551 | Value& currentValue(); 1552 | Char getNextChar(); 1553 | void 1554 | getLocationLineAndColumn(Location location, int& line, int& column) const; 1555 | JSONCPP_STRING getLocationLineAndColumn(Location location) const; 1556 | void addComment(Location begin, Location end, CommentPlacement placement); 1557 | void skipCommentTokens(Token& token); 1558 | 1559 | typedef std::stack Nodes; 1560 | Nodes nodes_; 1561 | Errors errors_; 1562 | JSONCPP_STRING document_; 1563 | Location begin_; 1564 | Location end_; 1565 | Location current_; 1566 | Location lastValueEnd_; 1567 | Value* lastValue_; 1568 | JSONCPP_STRING commentsBefore_; 1569 | Features features_; 1570 | bool collectComments_; 1571 | }; // Reader 1572 | 1573 | /** Interface for reading JSON from a char array. 1574 | */ 1575 | class JSON_API CharReader { 1576 | public: 1577 | virtual ~CharReader() {} 1578 | /** \brief Read a Value from a JSON 1579 | document. 1580 | * The document must be a UTF-8 encoded string containing the document to read. 1581 | * 1582 | * \param beginDoc Pointer on the beginning of the UTF-8 encoded string of the 1583 | document to read. 1584 | * \param endDoc Pointer on the end of the UTF-8 encoded string of the 1585 | document to read. 1586 | * Must be >= beginDoc. 1587 | * \param root [out] Contains the root value of the document if it was 1588 | * successfully parsed. 1589 | * \param errs [out] Formatted error messages (if not NULL) 1590 | * a user friendly string that lists errors in the parsed 1591 | * document. 1592 | * \return \c true if the document was successfully parsed, \c false if an 1593 | error occurred. 1594 | */ 1595 | virtual bool parse( 1596 | char const* beginDoc, char const* endDoc, 1597 | Value* root, JSONCPP_STRING* errs) = 0; 1598 | 1599 | class JSON_API Factory { 1600 | public: 1601 | virtual ~Factory() {} 1602 | /** \brief Allocate a CharReader via operator new(). 1603 | * \throw std::exception if something goes wrong (e.g. invalid settings) 1604 | */ 1605 | virtual CharReader* newCharReader() const = 0; 1606 | }; // Factory 1607 | }; // CharReader 1608 | 1609 | /** \brief Build a CharReader implementation. 1610 | 1611 | Usage: 1612 | \code 1613 | using namespace Json; 1614 | CharReaderBuilder builder; 1615 | builder["collectComments"] = false; 1616 | Value value; 1617 | JSONCPP_STRING errs; 1618 | bool ok = parseFromStream(builder, std::cin, &value, &errs); 1619 | \endcode 1620 | */ 1621 | class JSON_API CharReaderBuilder : public CharReader::Factory { 1622 | public: 1623 | // Note: We use a Json::Value so that we can add data-members to this class 1624 | // without a major version bump. 1625 | /** Configuration of this builder. 1626 | These are case-sensitive. 1627 | Available settings (case-sensitive): 1628 | - `"collectComments": false or true` 1629 | - true to collect comment and allow writing them 1630 | back during serialization, false to discard comments. 1631 | This parameter is ignored if allowComments is false. 1632 | - `"allowComments": false or true` 1633 | - true if comments are allowed. 1634 | - `"strictRoot": false or true` 1635 | - true if root must be either an array or an object value 1636 | - `"allowDroppedNullPlaceholders": false or true` 1637 | - true if dropped null placeholders are allowed. (See StreamWriterBuilder.) 1638 | - `"allowNumericKeys": false or true` 1639 | - true if numeric object keys are allowed. 1640 | - `"allowSingleQuotes": false or true` 1641 | - true if '' are allowed for strings (both keys and values) 1642 | - `"stackLimit": integer` 1643 | - Exceeding stackLimit (recursive depth of `readValue()`) will 1644 | cause an exception. 1645 | - This is a security issue (seg-faults caused by deeply nested JSON), 1646 | so the default is low. 1647 | - `"failIfExtra": false or true` 1648 | - If true, `parse()` returns false when extra non-whitespace trails 1649 | the JSON value in the input string. 1650 | - `"rejectDupKeys": false or true` 1651 | - If true, `parse()` returns false when a key is duplicated within an object. 1652 | - `"allowSpecialFloats": false or true` 1653 | - If true, special float values (NaNs and infinities) are allowed 1654 | and their values are lossfree restorable. 1655 | 1656 | You can examine 'settings_` yourself 1657 | to see the defaults. You can also write and read them just like any 1658 | JSON Value. 1659 | \sa setDefaults() 1660 | */ 1661 | Json::Value settings_; 1662 | 1663 | CharReaderBuilder(); 1664 | ~CharReaderBuilder() JSONCPP_OVERRIDE; 1665 | 1666 | CharReader* newCharReader() const JSONCPP_OVERRIDE; 1667 | 1668 | /** \return true if 'settings' are legal and consistent; 1669 | * otherwise, indicate bad settings via 'invalid'. 1670 | */ 1671 | bool validate(Json::Value* invalid) const; 1672 | 1673 | /** A simple way to update a specific setting. 1674 | */ 1675 | Value& operator[](JSONCPP_STRING key); 1676 | 1677 | /** Called by ctor, but you can use this to reset settings_. 1678 | * \pre 'settings' != NULL (but Json::null is fine) 1679 | * \remark Defaults: 1680 | * \snippet src/lib_json/json_reader.cpp CharReaderBuilderDefaults 1681 | */ 1682 | static void setDefaults(Json::Value* settings); 1683 | /** Same as old Features::strictMode(). 1684 | * \pre 'settings' != NULL (but Json::null is fine) 1685 | * \remark Defaults: 1686 | * \snippet src/lib_json/json_reader.cpp CharReaderBuilderStrictMode 1687 | */ 1688 | static void strictMode(Json::Value* settings); 1689 | }; 1690 | 1691 | /** Consume entire stream and use its begin/end. 1692 | * Someday we might have a real StreamReader, but for now this 1693 | * is convenient. 1694 | */ 1695 | bool JSON_API parseFromStream( 1696 | CharReader::Factory const&, 1697 | JSONCPP_ISTREAM&, 1698 | Value* root, std::string* errs); 1699 | 1700 | /** \brief Read from 'sin' into 'root'. 1701 | 1702 | Always keep comments from the input JSON. 1703 | 1704 | This can be used to read a file into a particular sub-object. 1705 | For example: 1706 | \code 1707 | Json::Value root; 1708 | cin >> root["dir"]["file"]; 1709 | cout << root; 1710 | \endcode 1711 | Result: 1712 | \verbatim 1713 | { 1714 | "dir": { 1715 | "file": { 1716 | // The input stream JSON would be nested here. 1717 | } 1718 | } 1719 | } 1720 | \endverbatim 1721 | \throw std::exception on parse error. 1722 | \see Json::operator<<() 1723 | */ 1724 | JSON_API JSONCPP_ISTREAM& operator>>(JSONCPP_ISTREAM&, Value&); 1725 | 1726 | } // namespace Json 1727 | 1728 | #pragma pack(pop) 1729 | 1730 | #if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) 1731 | #pragma warning(pop) 1732 | #endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) 1733 | 1734 | #endif // CPPTL_JSON_READER_H_INCLUDED 1735 | 1736 | // ////////////////////////////////////////////////////////////////////// 1737 | // End of content of file: include/json/reader.h 1738 | // ////////////////////////////////////////////////////////////////////// 1739 | 1740 | 1741 | 1742 | 1743 | 1744 | 1745 | // ////////////////////////////////////////////////////////////////////// 1746 | // Beginning of content of file: include/json/writer.h 1747 | // ////////////////////////////////////////////////////////////////////// 1748 | 1749 | // Copyright 2007-2010 Baptiste Lepilleur 1750 | // Distributed under MIT license, or public domain if desired and 1751 | // recognized in your jurisdiction. 1752 | // See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE 1753 | 1754 | #ifndef JSON_WRITER_H_INCLUDED 1755 | #define JSON_WRITER_H_INCLUDED 1756 | 1757 | #if !defined(JSON_IS_AMALGAMATION) 1758 | #include "value.h" 1759 | #endif // if !defined(JSON_IS_AMALGAMATION) 1760 | #include 1761 | #include 1762 | #include 1763 | 1764 | // Disable warning C4251: : needs to have dll-interface to 1765 | // be used by... 1766 | #if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) 1767 | #pragma warning(push) 1768 | #pragma warning(disable : 4251) 1769 | #endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) 1770 | 1771 | #pragma pack(push, 8) 1772 | 1773 | namespace Json { 1774 | 1775 | class Value; 1776 | 1777 | /** 1778 | 1779 | Usage: 1780 | \code 1781 | using namespace Json; 1782 | void writeToStdout(StreamWriter::Factory const& factory, Value const& value) { 1783 | std::unique_ptr const writer( 1784 | factory.newStreamWriter()); 1785 | writer->write(value, &std::cout); 1786 | std::cout << std::endl; // add lf and flush 1787 | } 1788 | \endcode 1789 | */ 1790 | class JSON_API StreamWriter { 1791 | protected: 1792 | JSONCPP_OSTREAM* sout_; // not owned; will not delete 1793 | public: 1794 | StreamWriter(); 1795 | virtual ~StreamWriter(); 1796 | /** Write Value into document as configured in sub-class. 1797 | Do not take ownership of sout, but maintain a reference during function. 1798 | \pre sout != NULL 1799 | \return zero on success (For now, we always return zero, so check the stream instead.) 1800 | \throw std::exception possibly, depending on configuration 1801 | */ 1802 | virtual int write(Value const& root, JSONCPP_OSTREAM* sout) = 0; 1803 | 1804 | /** \brief A simple abstract factory. 1805 | */ 1806 | class JSON_API Factory { 1807 | public: 1808 | virtual ~Factory(); 1809 | /** \brief Allocate a CharReader via operator new(). 1810 | * \throw std::exception if something goes wrong (e.g. invalid settings) 1811 | */ 1812 | virtual StreamWriter* newStreamWriter() const = 0; 1813 | }; // Factory 1814 | }; // StreamWriter 1815 | 1816 | /** \brief Write into stringstream, then return string, for convenience. 1817 | * A StreamWriter will be created from the factory, used, and then deleted. 1818 | */ 1819 | JSONCPP_STRING JSON_API writeString(StreamWriter::Factory const& factory, Value const& root); 1820 | 1821 | 1822 | /** \brief Build a StreamWriter implementation. 1823 | 1824 | Usage: 1825 | \code 1826 | using namespace Json; 1827 | Value value = ...; 1828 | StreamWriterBuilder builder; 1829 | builder["commentStyle"] = "None"; 1830 | builder["indentation"] = " "; // or whatever you like 1831 | std::unique_ptr writer( 1832 | builder.newStreamWriter()); 1833 | writer->write(value, &std::cout); 1834 | std::cout << std::endl; // add lf and flush 1835 | \endcode 1836 | */ 1837 | class JSON_API StreamWriterBuilder : public StreamWriter::Factory { 1838 | public: 1839 | // Note: We use a Json::Value so that we can add data-members to this class 1840 | // without a major version bump. 1841 | /** Configuration of this builder. 1842 | Available settings (case-sensitive): 1843 | - "commentStyle": "None" or "All" 1844 | - "indentation": "" 1845 | - "enableYAMLCompatibility": false or true 1846 | - slightly change the whitespace around colons 1847 | - "dropNullPlaceholders": false or true 1848 | - Drop the "null" string from the writer's output for nullValues. 1849 | Strictly speaking, this is not valid JSON. But when the output is being 1850 | fed to a browser's Javascript, it makes for smaller output and the 1851 | browser can handle the output just fine. 1852 | - "useSpecialFloats": false or true 1853 | - If true, outputs non-finite floating point values in the following way: 1854 | NaN values as "NaN", positive infinity as "Infinity", and negative infinity 1855 | as "-Infinity". 1856 | 1857 | You can examine 'settings_` yourself 1858 | to see the defaults. You can also write and read them just like any 1859 | JSON Value. 1860 | \sa setDefaults() 1861 | */ 1862 | Json::Value settings_; 1863 | 1864 | StreamWriterBuilder(); 1865 | ~StreamWriterBuilder() JSONCPP_OVERRIDE; 1866 | 1867 | /** 1868 | * \throw std::exception if something goes wrong (e.g. invalid settings) 1869 | */ 1870 | StreamWriter* newStreamWriter() const JSONCPP_OVERRIDE; 1871 | 1872 | /** \return true if 'settings' are legal and consistent; 1873 | * otherwise, indicate bad settings via 'invalid'. 1874 | */ 1875 | bool validate(Json::Value* invalid) const; 1876 | /** A simple way to update a specific setting. 1877 | */ 1878 | Value& operator[](JSONCPP_STRING key); 1879 | 1880 | /** Called by ctor, but you can use this to reset settings_. 1881 | * \pre 'settings' != NULL (but Json::null is fine) 1882 | * \remark Defaults: 1883 | * \snippet src/lib_json/json_writer.cpp StreamWriterBuilderDefaults 1884 | */ 1885 | static void setDefaults(Json::Value* settings); 1886 | }; 1887 | 1888 | /** \brief Abstract class for writers. 1889 | * \deprecated Use StreamWriter. (And really, this is an implementation detail.) 1890 | */ 1891 | class JSON_API Writer { 1892 | public: 1893 | virtual ~Writer(); 1894 | 1895 | virtual JSONCPP_STRING write(const Value& root) = 0; 1896 | }; 1897 | 1898 | /** \brief Outputs a Value in JSON format 1899 | *without formatting (not human friendly). 1900 | * 1901 | * The JSON document is written in a single line. It is not intended for 'human' 1902 | *consumption, 1903 | * but may be usefull to support feature such as RPC where bandwith is limited. 1904 | * \sa Reader, Value 1905 | * \deprecated Use StreamWriterBuilder. 1906 | */ 1907 | class JSON_API FastWriter : public Writer { 1908 | 1909 | public: 1910 | FastWriter(); 1911 | ~FastWriter() JSONCPP_OVERRIDE {} 1912 | 1913 | void enableYAMLCompatibility(); 1914 | 1915 | /** \brief Drop the "null" string from the writer's output for nullValues. 1916 | * Strictly speaking, this is not valid JSON. But when the output is being 1917 | * fed to a browser's Javascript, it makes for smaller output and the 1918 | * browser can handle the output just fine. 1919 | */ 1920 | void dropNullPlaceholders(); 1921 | 1922 | void omitEndingLineFeed(); 1923 | 1924 | public: // overridden from Writer 1925 | JSONCPP_STRING write(const Value& root) JSONCPP_OVERRIDE; 1926 | 1927 | private: 1928 | void writeValue(const Value& value); 1929 | 1930 | JSONCPP_STRING document_; 1931 | bool yamlCompatiblityEnabled_; 1932 | bool dropNullPlaceholders_; 1933 | bool omitEndingLineFeed_; 1934 | }; 1935 | 1936 | /** \brief Writes a Value in JSON format in a 1937 | *human friendly way. 1938 | * 1939 | * The rules for line break and indent are as follow: 1940 | * - Object value: 1941 | * - if empty then print {} without indent and line break 1942 | * - if not empty the print '{', line break & indent, print one value per 1943 | *line 1944 | * and then unindent and line break and print '}'. 1945 | * - Array value: 1946 | * - if empty then print [] without indent and line break 1947 | * - if the array contains no object value, empty array or some other value 1948 | *types, 1949 | * and all the values fit on one lines, then print the array on a single 1950 | *line. 1951 | * - otherwise, it the values do not fit on one line, or the array contains 1952 | * object or non empty array, then print one value per line. 1953 | * 1954 | * If the Value have comments then they are outputed according to their 1955 | *#CommentPlacement. 1956 | * 1957 | * \sa Reader, Value, Value::setComment() 1958 | * \deprecated Use StreamWriterBuilder. 1959 | */ 1960 | class JSON_API StyledWriter : public Writer { 1961 | public: 1962 | StyledWriter(); 1963 | ~StyledWriter() JSONCPP_OVERRIDE {} 1964 | 1965 | public: // overridden from Writer 1966 | /** \brief Serialize a Value in JSON format. 1967 | * \param root Value to serialize. 1968 | * \return String containing the JSON document that represents the root value. 1969 | */ 1970 | JSONCPP_STRING write(const Value& root) JSONCPP_OVERRIDE; 1971 | 1972 | private: 1973 | void writeValue(const Value& value); 1974 | void writeArrayValue(const Value& value); 1975 | bool isMultineArray(const Value& value); 1976 | void pushValue(const JSONCPP_STRING& value); 1977 | void writeIndent(); 1978 | void writeWithIndent(const JSONCPP_STRING& value); 1979 | void indent(); 1980 | void unindent(); 1981 | void writeCommentBeforeValue(const Value& root); 1982 | void writeCommentAfterValueOnSameLine(const Value& root); 1983 | bool hasCommentForValue(const Value& value); 1984 | static JSONCPP_STRING normalizeEOL(const JSONCPP_STRING& text); 1985 | 1986 | typedef std::vector ChildValues; 1987 | 1988 | ChildValues childValues_; 1989 | JSONCPP_STRING document_; 1990 | JSONCPP_STRING indentString_; 1991 | unsigned int rightMargin_; 1992 | unsigned int indentSize_; 1993 | bool addChildValues_; 1994 | }; 1995 | 1996 | /** \brief Writes a Value in JSON format in a 1997 | human friendly way, 1998 | to a stream rather than to a string. 1999 | * 2000 | * The rules for line break and indent are as follow: 2001 | * - Object value: 2002 | * - if empty then print {} without indent and line break 2003 | * - if not empty the print '{', line break & indent, print one value per 2004 | line 2005 | * and then unindent and line break and print '}'. 2006 | * - Array value: 2007 | * - if empty then print [] without indent and line break 2008 | * - if the array contains no object value, empty array or some other value 2009 | types, 2010 | * and all the values fit on one lines, then print the array on a single 2011 | line. 2012 | * - otherwise, it the values do not fit on one line, or the array contains 2013 | * object or non empty array, then print one value per line. 2014 | * 2015 | * If the Value have comments then they are outputed according to their 2016 | #CommentPlacement. 2017 | * 2018 | * \param indentation Each level will be indented by this amount extra. 2019 | * \sa Reader, Value, Value::setComment() 2020 | * \deprecated Use StreamWriterBuilder. 2021 | */ 2022 | class JSON_API StyledStreamWriter { 2023 | public: 2024 | StyledStreamWriter(JSONCPP_STRING indentation = "\t"); 2025 | ~StyledStreamWriter() {} 2026 | 2027 | public: 2028 | /** \brief Serialize a Value in JSON format. 2029 | * \param out Stream to write to. (Can be ostringstream, e.g.) 2030 | * \param root Value to serialize. 2031 | * \note There is no point in deriving from Writer, since write() should not 2032 | * return a value. 2033 | */ 2034 | void write(JSONCPP_OSTREAM& out, const Value& root); 2035 | 2036 | private: 2037 | void writeValue(const Value& value); 2038 | void writeArrayValue(const Value& value); 2039 | bool isMultineArray(const Value& value); 2040 | void pushValue(const JSONCPP_STRING& value); 2041 | void writeIndent(); 2042 | void writeWithIndent(const JSONCPP_STRING& value); 2043 | void indent(); 2044 | void unindent(); 2045 | void writeCommentBeforeValue(const Value& root); 2046 | void writeCommentAfterValueOnSameLine(const Value& root); 2047 | bool hasCommentForValue(const Value& value); 2048 | static JSONCPP_STRING normalizeEOL(const JSONCPP_STRING& text); 2049 | 2050 | typedef std::vector ChildValues; 2051 | 2052 | ChildValues childValues_; 2053 | JSONCPP_OSTREAM* document_; 2054 | JSONCPP_STRING indentString_; 2055 | unsigned int rightMargin_; 2056 | JSONCPP_STRING indentation_; 2057 | bool addChildValues_ : 1; 2058 | bool indented_ : 1; 2059 | }; 2060 | 2061 | #if defined(JSON_HAS_INT64) 2062 | JSONCPP_STRING JSON_API valueToString(Int value); 2063 | JSONCPP_STRING JSON_API valueToString(UInt value); 2064 | #endif // if defined(JSON_HAS_INT64) 2065 | JSONCPP_STRING JSON_API valueToString(LargestInt value); 2066 | JSONCPP_STRING JSON_API valueToString(LargestUInt value); 2067 | JSONCPP_STRING JSON_API valueToString(double value); 2068 | JSONCPP_STRING JSON_API valueToString(bool value); 2069 | JSONCPP_STRING JSON_API valueToQuotedString(const char* value); 2070 | 2071 | /// \brief Output using the StyledStreamWriter. 2072 | /// \see Json::operator>>() 2073 | JSON_API JSONCPP_OSTREAM& operator<<(JSONCPP_OSTREAM&, const Value& root); 2074 | 2075 | } // namespace Json 2076 | 2077 | #pragma pack(pop) 2078 | 2079 | #if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) 2080 | #pragma warning(pop) 2081 | #endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) 2082 | 2083 | #endif // JSON_WRITER_H_INCLUDED 2084 | 2085 | // ////////////////////////////////////////////////////////////////////// 2086 | // End of content of file: include/json/writer.h 2087 | // ////////////////////////////////////////////////////////////////////// 2088 | 2089 | 2090 | 2091 | 2092 | 2093 | 2094 | // ////////////////////////////////////////////////////////////////////// 2095 | // Beginning of content of file: include/json/assertions.h 2096 | // ////////////////////////////////////////////////////////////////////// 2097 | 2098 | // Copyright 2007-2010 Baptiste Lepilleur 2099 | // Distributed under MIT license, or public domain if desired and 2100 | // recognized in your jurisdiction. 2101 | // See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE 2102 | 2103 | #ifndef CPPTL_JSON_ASSERTIONS_H_INCLUDED 2104 | #define CPPTL_JSON_ASSERTIONS_H_INCLUDED 2105 | 2106 | #include 2107 | #include 2108 | 2109 | #if !defined(JSON_IS_AMALGAMATION) 2110 | #include "config.h" 2111 | #endif // if !defined(JSON_IS_AMALGAMATION) 2112 | 2113 | /** It should not be possible for a maliciously designed file to 2114 | * cause an abort() or seg-fault, so these macros are used only 2115 | * for pre-condition violations and internal logic errors. 2116 | */ 2117 | #if JSON_USE_EXCEPTION 2118 | 2119 | // @todo <= add detail about condition in exception 2120 | # define JSON_ASSERT(condition) \ 2121 | {if (!(condition)) {Json::throwLogicError( "assert json failed" );}} 2122 | 2123 | # define JSON_FAIL_MESSAGE(message) \ 2124 | { \ 2125 | JSONCPP_OSTRINGSTREAM oss; oss << message; \ 2126 | Json::throwLogicError(oss.str()); \ 2127 | abort(); \ 2128 | } 2129 | 2130 | #else // JSON_USE_EXCEPTION 2131 | 2132 | # define JSON_ASSERT(condition) assert(condition) 2133 | 2134 | // The call to assert() will show the failure message in debug builds. In 2135 | // release builds we abort, for a core-dump or debugger. 2136 | # define JSON_FAIL_MESSAGE(message) \ 2137 | { \ 2138 | JSONCPP_OSTRINGSTREAM oss; oss << message; \ 2139 | assert(false && oss.str().c_str()); \ 2140 | abort(); \ 2141 | } 2142 | 2143 | 2144 | #endif 2145 | 2146 | #define JSON_ASSERT_MESSAGE(condition, message) \ 2147 | if (!(condition)) { \ 2148 | JSON_FAIL_MESSAGE(message); \ 2149 | } 2150 | 2151 | #endif // CPPTL_JSON_ASSERTIONS_H_INCLUDED 2152 | 2153 | // ////////////////////////////////////////////////////////////////////// 2154 | // End of content of file: include/json/assertions.h 2155 | // ////////////////////////////////////////////////////////////////////// 2156 | 2157 | 2158 | 2159 | 2160 | 2161 | #endif //ifndef JSON_AMALGATED_H_INCLUDED 2162 | --------------------------------------------------------------------------------