├── .gitattributes ├── .gitignore ├── Mods ├── BackpackManager │ └── __init__.py ├── ModMenu │ ├── DeprecationHelper.py │ ├── HookManager.py │ ├── KeybindManager.py │ ├── MenuManager.py │ ├── ModObjects.py │ ├── NetworkManager.py │ ├── OptionManager.py │ ├── Options.py │ ├── SettingsManager.py │ └── __init__.py ├── Quickload │ └── __init__.py ├── ReadOnly │ └── __init__.py ├── SkillRandomizer │ └── __init__.py ├── __init__.py ├── linters.txt └── setup.cfg ├── PythonSDK.sln ├── PythonSDK.sln.DotSettings.user ├── PythonSDK.vcxproj ├── PythonSDK.vcxproj.filters ├── README.md ├── scripts ├── add_init.py ├── add_static.py ├── distribute_tarrays.py ├── fix_classes.py ├── fix_fuckup.py ├── fix_funcs.py ├── fix_pointers.py ├── fix_references.py ├── gen_funcs.py ├── merge.py ├── prune.py ├── readd_bitfields.py ├── regen_pydefs.py ├── regen_statics.py ├── setup_polymorphism.py ├── sort_relationships.py ├── split_sdk.py └── tarray_finder.py └── src ├── AntiDebug.cpp ├── CHookManager.cpp ├── CPythonInterface.cpp ├── CSigScan.cpp ├── CSimpleDetour.cpp ├── CoreExtensions.cpp ├── Logging.cpp ├── PackageFix.cpp ├── Settings.cpp ├── UnrealEngine ├── Core_functions.cpp └── Engine_functions.cpp ├── UnrealSDK.cpp ├── Util.cpp ├── include ├── AntiDebug.h ├── CHookManager.h ├── CPythonInterface.h ├── CSigScan.h ├── CSimpleDetour.h ├── Exceptions.h ├── Exports.h ├── Games.h ├── Logging.h ├── MemoryDebug.h ├── MemorySignature.h ├── Settings.h ├── Signatures.h ├── TypeMap.h ├── UnrealEngine │ ├── Core │ │ ├── Core_classes.h │ │ ├── Core_f_structs.h │ │ └── Core_structs.h │ └── Engine │ │ ├── Engine_classes.h │ │ ├── Engine_f_structs.h │ │ └── Engine_structs.h ├── UnrealSDK.h ├── Util.h ├── detours │ ├── detours.cpp │ ├── detours.h │ ├── detver.h │ ├── disasm.cpp │ └── modules.cpp ├── gamedefines.h ├── pybind11 │ ├── attr.h │ ├── buffer_info.h │ ├── cast.h │ ├── chrono.h │ ├── common.h │ ├── complex.h │ ├── detail │ │ ├── class.h │ │ ├── common.h │ │ ├── descr.h │ │ ├── init.h │ │ ├── internals.h │ │ └── typeid.h │ ├── eigen.h │ ├── embed.h │ ├── eval.h │ ├── functional.h │ ├── iostream.h │ ├── numpy.h │ ├── operators.h │ ├── options.h │ ├── pybind11.h │ ├── pytypes.h │ ├── stl.h │ └── stl_bind.h ├── pydef.h ├── stb_image.h └── stdafx.h ├── main.cpp ├── pydefs ├── Core_classes.cpp ├── Core_structs.cpp ├── _TArray.cpp └── gamedefines.cpp └── stdafx.cpp /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | [Dd]ebug 2 | [Rr]elease 3 | .vs 4 | *.dll 5 | *.pyc 6 | *.json 7 | *.vcxproj.user -------------------------------------------------------------------------------- /Mods/BackpackManager/__init__.py: -------------------------------------------------------------------------------- 1 | import unrealsdk 2 | 3 | from Mods.ModMenu import EnabledSaveType, Hook, ModTypes, Options, RegisterMod, SDKMod 4 | 5 | 6 | class BackpackManager(SDKMod): 7 | Name: str = "Backpack Manager" 8 | Author = "FromDarkHell" 9 | Description: str = "Customize the size of your character's backpack on the fly!" 10 | Version: str = "1.1" 11 | 12 | Types: ModTypes = ModTypes.Gameplay 13 | SaveEnabledState: EnabledSaveType = EnabledSaveType.LoadWithSettings 14 | 15 | BackpackSize: Options.Slider = Options.Slider( 16 | "Backpack", "Change the size of your character's backpack
Default is 39", 39, 0, 200, 1 17 | ) 18 | Options = [BackpackSize] 19 | 20 | @Hook("WillowGame.WillowHUD.CreateWeaponScopeMovie") 21 | def _GameLoad(self, caller: unrealsdk.UObject, function: unrealsdk.UFunction, params: unrealsdk.FStruct) -> bool: 22 | PC = unrealsdk.GetEngine().GamePlayers[0].Actor 23 | if PC and PC.Pawn: 24 | PC.Pawn.InvManager.InventorySlotMax_Misc = self.BackpackSize.CurrentValue 25 | return True 26 | 27 | def ModOptionChanged(self, option, newValue) -> None: 28 | if option == self.BackpackSize: 29 | PC = unrealsdk.GetEngine().GamePlayers[0].Actor 30 | if PC and PC.Pawn: 31 | PC.Pawn.InvManager.InventorySlotMax_Misc = newValue 32 | 33 | 34 | RegisterMod(BackpackManager()) 35 | -------------------------------------------------------------------------------- /Mods/ModMenu/DeprecationHelper.py: -------------------------------------------------------------------------------- 1 | import unrealsdk 2 | import functools 3 | from typing import Any, Callable, Dict, List, Optional, Set, Tuple 4 | 5 | __all__: Tuple[str, ...] = ( 6 | "Deprecated", 7 | "NameChangeMsg", 8 | "PrintWarning", 9 | ) 10 | 11 | _printed_deprecation_warnings: Set[str] = set() 12 | 13 | 14 | def PrintWarning(msg: str) -> None: 15 | """ 16 | Prints a warning containing the provided message. Will only happen once per message. 17 | 18 | Args: 19 | msg: The message to print. 20 | """ 21 | if msg not in _printed_deprecation_warnings: 22 | _printed_deprecation_warnings.add(msg) 23 | unrealsdk.Log(f"[Warning] {msg}") 24 | 25 | 26 | def NameChangeMsg(old_name: str, new_name: str) -> str: 27 | """ 28 | Helper returning a generic deprecation message for when something's name changed. 29 | 30 | Args: 31 | old_name: The deprecated name. 32 | new_name: The new name. 33 | Returns: 34 | The name change deprecation message. 35 | """ 36 | return f"Use of '{old_name}' is deprecated, use '{new_name}' instead." 37 | 38 | 39 | def Deprecated( 40 | msg: str, 41 | func: Optional[Callable[..., Any]] = None 42 | ) -> Callable[..., Any]: 43 | """ 44 | Decorator that prints a deprecation message when it's wrapped function is called. 45 | 46 | Can also be called with the function as an argument. 47 | 48 | Args: 49 | msg: The message to print. 50 | func: The function to wrap. 51 | Returns: 52 | The wrapped function 53 | """ 54 | def decorator(old_func: Callable[..., Any]) -> Callable[..., Any]: 55 | @functools.wraps(old_func) 56 | def new_func(*args: List[Any], **kwargs: Dict[str, Any]) -> Any: 57 | PrintWarning(msg) 58 | return old_func(*args, **kwargs) 59 | return new_func 60 | 61 | if func is None: 62 | return decorator 63 | else: 64 | return decorator(func) 65 | -------------------------------------------------------------------------------- /Mods/ModMenu/HookManager.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | import unrealsdk 4 | import functools 5 | import weakref 6 | from inspect import Parameter, signature 7 | from typing import Any, Callable, Optional, Tuple, Union 8 | 9 | __all__: Tuple[str, ...] = ( 10 | "AnyHook", 11 | "Hook", 12 | "HookFunction", 13 | "HookMethod", 14 | "RegisterHooks", 15 | "RemoveHooks", 16 | ) 17 | 18 | 19 | HookFunction = Callable[ 20 | [unrealsdk.UObject, unrealsdk.UFunction, unrealsdk.FStruct], 21 | Optional[bool] 22 | ] 23 | HookMethod = Callable[ 24 | [Any, unrealsdk.UObject, unrealsdk.UFunction, unrealsdk.FStruct], 25 | Optional[bool] 26 | ] 27 | AnyHook = Union[HookFunction, HookMethod] 28 | 29 | 30 | def Hook(target: str, name: str = "{0}.{1}") -> Callable[[AnyHook], AnyHook]: 31 | """ 32 | A decorator for functions that should be invoked in response to an Unreal Engine method's 33 | invokation. 34 | 35 | The function being decorated may be a standalone function, in which case its signature must 36 | match that of `unrealsdk.RegisterHook` functions: 37 | (caller: unrealsdk.UObject, function: unrealsdk.UFunction, params: unrealsdk.FStruct) 38 | 39 | Alternatively, the function may be an instance method of any object. In this case, the hook will 40 | be activated once `ModMenu.RegisterHooks(object)` has been called on the object. The signature 41 | of the method must match that of `unrealsdk.RegisterHook` functions, with the addition of `self` 42 | as the first parameter: 43 | (self, caller: unrealsdk.UObject, function: unrealsdk.UFunction, params: unrealsdk.FStruct) 44 | 45 | Upon invokation of the Unreal Engine method, the decorated function will be called. Its `caller` 46 | argument will contain the Unreal Engine object whose method was invoked, the `function` argument 47 | will contain the Unreal Engine function that was invoked, and the `params` argument will contain 48 | an `FStruct` with the arguments passed to the method. 49 | 50 | Args: 51 | target: 52 | A string representing the Unreal Engine method that should be hooked, in the format 53 | "..". 54 | name: 55 | A string which, when paired with the hook target, uniquely identifies this hook within 56 | the SDK. By default, a name is generated using the function's module, qualified name, 57 | and `id()` (in the case of mod instance method hooks, the mod instance's `id()` is used 58 | instead). 59 | 60 | If a custom name is provided, it may be a simple string, or a format string with either 61 | one or two replacement tokens. Token `{0}` will contain the function's module name and 62 | qualified name, separated by a ".". Argument `{1}` will contain the `id()` of the 63 | function or mod instance. 64 | """ 65 | def apply_hook(function: AnyHook) -> AnyHook: 66 | # If the function has four parameters, it should be a method. 67 | params = signature(function).parameters 68 | is_method = (len(params) == 4) 69 | 70 | # Retrieve the function's dictionary of targets. If it does not yet have one, we preform 71 | # initial setup on it now. 72 | hook_targets = getattr(function, "HookTargets", None) 73 | if hook_targets is None: 74 | param_exception = ValueError( 75 | "Hook functions must have the signature" 76 | " ([self,] caller: unrealsdk.UObject, function: unrealsdk.UFunction, params: unrealsdk.FStruct)" 77 | ) 78 | 79 | # If the function is an instance method, create a mutable list of the parameters and 80 | # remove the `self` one, so we may check the remaining ones same as a non-method. 81 | param_list = list(params.values()) 82 | if is_method: 83 | del param_list[0] 84 | # If the function has neither 4 nor 3 parameters, it is invalid. 85 | elif len(param_list) != 3: 86 | raise param_exception 87 | # If the functions parameters do not accept positional arguments, it is invalid. 88 | for param in param_list: 89 | if Parameter.POSITIONAL_ONLY != param.kind != Parameter.POSITIONAL_OR_KEYWORD: 90 | raise param_exception 91 | 92 | # If the function is a method, store the name format string on it for formatting with 93 | # future instances. If it's a simple function, format its name for use now. 94 | function.HookName = name if is_method else name.format( # type: ignore 95 | f"{function.__module__}.{function.__qualname__}", id(function) 96 | ) 97 | 98 | # With the function now known as valid, create its set of targets. 99 | hook_targets = function.HookTargets = set() # type: ignore 100 | 101 | hook_targets.add(target) 102 | 103 | if not is_method: 104 | unrealsdk.RunHook(target, function.HookName, function) # type: ignore 105 | 106 | return function 107 | return apply_hook 108 | 109 | 110 | def _create_method_wrapper(obj_ref: weakref.ReferenceType[object], obj_function: HookMethod) -> HookFunction: 111 | """Return a "true" function for the given bound method, passable to `unrealsdk.RegisterHook`.""" 112 | @functools.wraps(obj_function) 113 | def method_wrapper(caller: unrealsdk.UObject, function: unrealsdk.UFunction, params: unrealsdk.FStruct) -> Any: 114 | obj = obj_ref() 115 | method = obj_function.__get__(obj, type(obj)) # type: ignore 116 | return method(caller, obj_function, params) 117 | return method_wrapper 118 | 119 | 120 | def RegisterHooks(obj: object) -> None: 121 | """ 122 | Registers all `@Hook` decorated methods for the object. Said methods will subsequently be called 123 | in response to the hooked Unreal Engine methods. 124 | 125 | Args: 126 | obj: The object for which to register method hooks. 127 | """ 128 | 129 | # Create a weak reference to the object which we may use in attributes on it without creating 130 | # cyclical references. Before destruction, `RemoveHooks` should be called on the object to 131 | # ensure there are no remaining hooks that reference it. 132 | obj_ref = weakref.ref(obj, RemoveHooks) 133 | 134 | # Iterate over each attribute on the object's class that contains a function. 135 | for attribute_name, function in type(obj).__dict__.items(): 136 | if not callable(function): 137 | continue 138 | 139 | # Attempt to get the set of hook targets from the function. If it doesn't have one, or if 140 | # its signature doesn't have 4 parameters, it is not a hook method. 141 | hook_targets = getattr(function, "HookTargets", None) 142 | if hook_targets is None or len(signature(function).parameters) != 4: 143 | continue 144 | 145 | # Create a wrapper to replace the descriptor of the attribute, "binding" the function to the 146 | # mod's weak reference, in a function that can be passed to `unrealsdk.RunHook`. 147 | method_wrapper = _create_method_wrapper(obj_ref, function) 148 | setattr(obj, attribute_name, method_wrapper) 149 | 150 | # Format the provided hook name. 151 | method_wrapper.HookName = function.HookName.format( # type: ignore 152 | f"{function.__module__}.{function.__qualname__}", id(obj) 153 | ) 154 | 155 | for target in hook_targets: 156 | unrealsdk.RunHook(target, method_wrapper.HookName, method_wrapper) # type: ignore 157 | 158 | 159 | def RemoveHooks(obj: object) -> None: 160 | """ 161 | Unregisters all `@Hook` decorated methods for the object. Said methods will no longer be called 162 | in response to the hooked Unreal Engine methods. 163 | 164 | Args: 165 | obj: The object for which to unregister method hooks. 166 | """ 167 | for function in obj.__dict__.values(): 168 | if not callable(function): 169 | continue 170 | 171 | hook_targets = getattr(function, "HookTargets", None) 172 | if hook_targets is None: 173 | continue 174 | 175 | for target in hook_targets: 176 | unrealsdk.RemoveHook(target, function.HookName) 177 | -------------------------------------------------------------------------------- /Mods/ModMenu/OptionManager.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | import unrealsdk 4 | from typing import Any, List, Sequence, Tuple 5 | 6 | from . import MenuManager, ModObjects, Options, SettingsManager 7 | 8 | __all__: Tuple[str, ...] = () 9 | 10 | 11 | _modded_data_provider_stack: List[unrealsdk.UObject] = [] 12 | _nested_options_stack: List[Options.Nested] = [] 13 | 14 | _MOD_OPTIONS_EVENT_ID: int = 1417 15 | _MOD_OPTIONS_MENU_NAME: str = "MODS" 16 | 17 | _INDENT: int = 2 18 | 19 | 20 | class _ModHeader(Options.Field): 21 | def __init__(self, Caption: str) -> None: 22 | self.Caption = Caption 23 | self.Description = "" 24 | self.IsHidden = False 25 | 26 | 27 | def _create_data_provider(name: str) -> unrealsdk.UObject: 28 | """ 29 | Helper function that creates a new data provider and adds it to the stack. 30 | 31 | Args: 32 | name: The menu name to give the new data provider. 33 | Returns: 34 | The data provider. 35 | """ 36 | provider = unrealsdk.ConstructObject( 37 | Class=unrealsdk.FindClass("WillowScrollingListDataProviderOptionsBase") 38 | ) 39 | # See issue #45 40 | unrealsdk.GetEngine().GamePlayers[0].Actor.ServerRCon( 41 | f"set {provider.PathName(provider)} MenuDisplayName {name}" 42 | ) 43 | _modded_data_provider_stack.append(provider) 44 | return provider 45 | 46 | 47 | def _is_anything_shown(option_list: Sequence[Options.Base]) -> bool: 48 | """ 49 | Helper function that recursively checks if anything in the provided option list is shown. 50 | 51 | Args: 52 | option_list: The list of options to check. 53 | Returns: 54 | True if at least one of the options in the list, or in any nested lists, is shown. 55 | """ 56 | for option in option_list: 57 | if option.IsHidden: 58 | continue 59 | if isinstance(option, Options.Nested): 60 | if _is_anything_shown(option.Children): 61 | return True 62 | else: 63 | return True 64 | return False 65 | 66 | 67 | def _TopLevelOptionsPopulate(caller: unrealsdk.UObject, function: unrealsdk.UFunction, params: unrealsdk.FStruct) -> bool: 68 | """ This function is called to create the options menu. We use it to inject our `MODS` menu. """ 69 | # If not mods have accessable options, we want to disable the mods entry 70 | disabled = True 71 | for mod in ModObjects.Mods: 72 | if not mod.IsEnabled: 73 | continue 74 | if _is_anything_shown(mod.Options): 75 | disabled = False 76 | break 77 | 78 | def AddListItem(caller: unrealsdk.UObject, function: unrealsdk.UFunction, params: unrealsdk.FStruct) -> bool: 79 | """ 80 | This function is called every time an item is added to *any* menu list - we obviously can't 81 | use a generic hook. 82 | Using it cause it simplifies the code to add our own entry. 83 | """ 84 | if params.Caption == "$WillowGame.WillowScrollingList.BackCaption": 85 | caller.AddListItem(_MOD_OPTIONS_EVENT_ID, _MOD_OPTIONS_MENU_NAME, disabled, False) 86 | 87 | return True 88 | 89 | unrealsdk.RunHook("WillowGame.WillowScrollingList.AddListItem", "ModMenu.OptionManager", AddListItem) 90 | 91 | unrealsdk.DoInjectedCallNext() 92 | caller.Populate(params.TheList) 93 | 94 | unrealsdk.RemoveHook("WillowGame.WillowScrollingList.AddListItem", "ModMenu.OptionManager") 95 | return False 96 | 97 | 98 | def _WillowScrollingListOnClikEvent(caller: unrealsdk.UObject, function: unrealsdk.UFunction, params: unrealsdk.FStruct) -> bool: 99 | """ 100 | This function is called on a few different events to do with these scrolling lists. We're 101 | interested in it to detect when you open up one of our modded menus. 102 | """ 103 | global isMenuPluginMenu 104 | 105 | if params.Data.Type != "itemClick": 106 | return True 107 | 108 | # For some reason `caller.GetCurrentDataProvider()` returns a null object? 109 | provider = None 110 | for obj in caller.DataProviderStack: 111 | provider = obj.DataProvider.ObjectPointer 112 | if provider is None: 113 | return True 114 | 115 | if provider in _modded_data_provider_stack: 116 | # If you pressed the back button 117 | if params.Data.Index == len(_nested_options_stack[-1].Children): 118 | return True 119 | 120 | option = _nested_options_stack[-1].Children[params.Data.Index] 121 | if isinstance(option, Options.Nested): 122 | _nested_options_stack.append(option) 123 | caller.MyOwnerMovie.PlayUISound("MenuOpen") 124 | caller.PushDataProvider(_create_data_provider(option.Caption)) 125 | return False 126 | elif isinstance(option, Options.Field): 127 | return False 128 | 129 | elif ( 130 | provider.Class.Name == "WillowScrollingListDataProviderTopLevelOptions" 131 | and caller.IndexToEventId[params.Data.Index] == _MOD_OPTIONS_EVENT_ID 132 | ): 133 | caller.MyOwnerMovie.PlayUISound("MenuOpen") 134 | caller.PushDataProvider(_create_data_provider(_MOD_OPTIONS_MENU_NAME)) 135 | return False 136 | 137 | return True 138 | 139 | 140 | def _DataProviderOptionsBasePopulate(caller: unrealsdk.UObject, function: unrealsdk.UFunction, params: unrealsdk.FStruct) -> bool: 141 | """ 142 | This function is called to fill in a few of a scrolling lists. Our custom data providers are of 143 | this type, so we use it to populate the lists ourselves. 144 | """ 145 | if caller not in _modded_data_provider_stack: 146 | return True 147 | 148 | # If we're on the first level we need to setup the inital list 149 | if len(_nested_options_stack) == 0: 150 | all_options: List[Options.Base] = [] 151 | for mod in MenuManager.GetOrderedModList(): 152 | if not mod.IsEnabled: 153 | continue 154 | 155 | one_shown = False 156 | for option in mod.Options: 157 | if option.IsHidden: 158 | continue 159 | if not one_shown: 160 | one_shown = True 161 | all_options.append(_ModHeader(mod.Name)) 162 | all_options.append(option) 163 | 164 | _nested_options_stack.append(Options.Nested(_MOD_OPTIONS_MENU_NAME, "", all_options)) 165 | 166 | first_level = len(_nested_options_stack) == 1 167 | for idx, option in enumerate(_nested_options_stack[-1].Children): 168 | if option.IsHidden: 169 | continue 170 | 171 | indent = " " * _INDENT if first_level and not isinstance(option, _ModHeader) else "" 172 | 173 | if isinstance(option, Options.Spinner): 174 | spinner_idx: int 175 | if isinstance(option, Options.Boolean): 176 | spinner_idx = int(option.CurrentValue) 177 | else: 178 | spinner_idx = option.Choices.index(option.CurrentValue) 179 | 180 | params.TheList.AddSpinnerListItem( 181 | idx, indent + option.Caption, False, spinner_idx, option.Choices 182 | ) 183 | elif isinstance(option, Options.Slider): 184 | params.TheList.AddSliderListItem( 185 | idx, 186 | indent + option.Caption, 187 | False, 188 | option.CurrentValue, 189 | option.MinValue, 190 | option.MaxValue, 191 | option.Increment, 192 | ) 193 | elif isinstance(option, Options.Field): 194 | disabled = False 195 | if isinstance(option, Options.Nested): 196 | disabled = not _is_anything_shown(option.Children) 197 | params.TheList.AddListItem(idx, indent + option.Caption, disabled, False) 198 | 199 | caller.AddDescription(idx, option.Description) 200 | 201 | return False 202 | 203 | 204 | def _DataProviderOptionsBaseOnPop(caller: unrealsdk.UObject, function: unrealsdk.UFunction, params: unrealsdk.FStruct) -> bool: 205 | """ 206 | This function is called when the data provider is popped off the stack, when you leave the menu. 207 | Unsuprisingly, we do the same with our stacks. We can also use it to save settings when you 208 | leave the outermost menu. 209 | """ 210 | if caller in _modded_data_provider_stack: 211 | _modded_data_provider_stack.pop() 212 | _nested_options_stack.pop() 213 | if len(_modded_data_provider_stack) == 0: 214 | SettingsManager.SaveAllModSettings() 215 | 216 | return True 217 | 218 | 219 | def _HandleSpinnerSliderChange(caller: unrealsdk.UObject, function: unrealsdk.UFunction, params: unrealsdk.FStruct) -> bool: 220 | """ 221 | The two functions that have this hook get called when a spinner or slider changes value. We use 222 | the hook to update our version of the objects. 223 | """ 224 | if caller not in _modded_data_provider_stack: 225 | return True 226 | 227 | changed_option = _nested_options_stack[-1].Children[params.EventID] 228 | 229 | new_value: Any 230 | if isinstance(changed_option, Options.Slider): 231 | new_value = int(params.NewSliderValue) 232 | elif isinstance(changed_option, Options.Boolean): 233 | new_value = bool(params.NewChoiceIndex) 234 | elif isinstance(changed_option, Options.Spinner): 235 | new_value = changed_option.Choices[params.NewChoiceIndex] 236 | else: 237 | raise RuntimeError(f"Option of bad type '{type(changed_option)}' somehow changed value.") 238 | 239 | def in_option_list(option_list: Sequence[Options.Base]) -> bool: 240 | return any( 241 | in_option_list(option.Children) 242 | if isinstance(option, Options.Nested) 243 | else option == changed_option 244 | for option in option_list 245 | ) 246 | 247 | for mod in ModObjects.Mods: 248 | if in_option_list(mod.Options): 249 | # Calling this before updating the value 250 | mod.ModOptionChanged(changed_option, new_value) 251 | changed_option.CurrentValue = new_value 252 | break 253 | 254 | return True 255 | 256 | 257 | unrealsdk.RunHook("WillowGame.WillowScrollingListDataProviderTopLevelOptions.Populate", "ModMenu.OptionManager", _TopLevelOptionsPopulate) 258 | unrealsdk.RunHook("WillowGame.WillowScrollingList.OnClikEvent", "ModMenu.OptionManager", _WillowScrollingListOnClikEvent) 259 | unrealsdk.RunHook("WillowGame.WillowScrollingListDataProviderOptionsBase.Populate", "ModMenu.OptionManager", _DataProviderOptionsBasePopulate) 260 | unrealsdk.RunHook("WillowGame.WillowScrollingListDataProviderOptionsBase.OnPop", "ModMenu.OptionManager", _DataProviderOptionsBaseOnPop) 261 | unrealsdk.RunHook("WillowGame.WillowScrollingListDataProviderOptionsBase.HandleSpinnerChange", "ModMenu.OptionManager", _HandleSpinnerSliderChange) 262 | unrealsdk.RunHook("WillowGame.WillowScrollingListDataProviderOptionsBase.HandleSliderChange", "ModMenu.OptionManager", _HandleSpinnerSliderChange) 263 | -------------------------------------------------------------------------------- /Mods/ModMenu/SettingsManager.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | import unrealsdk 4 | import inspect 5 | import json 6 | import traceback 7 | from os import path 8 | from typing import Any, Dict, Sequence, Set, Tuple 9 | 10 | from . import DeprecationHelper as dh 11 | from . import KeybindManager, ModObjects, Options 12 | 13 | __all__: Tuple[str, ...] = ( 14 | "GetSettingsFilePath", 15 | "SaveModSettings", 16 | "SaveAllModSettings", 17 | "LoadModSettings", 18 | ) 19 | 20 | 21 | _OPTIONS_CATEGORY_NAME = "Options" 22 | _KEYBINDS_CATEGORY_NAME = "Keybinds" 23 | _ENABLED_CATEGORY_NAME = "AutoEnable" 24 | _SETTINGS_FILE_NAME = "settings.json" 25 | 26 | _mods_to_enable_on_main_menu: Set[ModObjects.SDKMod] = set() 27 | 28 | 29 | def GetSettingsFilePath(mod: ModObjects.SDKMod) -> str: 30 | """ 31 | Gets the path of a mod's settings file. 32 | 33 | Args: 34 | mod: The instance of the mod whose settings file path should be retrived. 35 | Returns: 36 | The path to the file, which is in the same folder as the file defining the mod class. 37 | """ 38 | return path.join(path.dirname(inspect.getfile(mod.__class__)), _SETTINGS_FILE_NAME) 39 | 40 | 41 | def SaveModSettings(mod: ModObjects.SDKMod) -> None: 42 | """ 43 | Saves the options, keybinds, and enabled state of a mod, where applicable. 44 | 45 | Args: 46 | mod: The instance of the mod whose settings should be saved. 47 | """ 48 | mod_settings: Dict[str, Any] = {} 49 | 50 | def create_options_dict(options: Sequence[Options.Base]) -> Dict[str, Any]: 51 | settings = {} 52 | for option in options: 53 | if isinstance(option, Options.Value): 54 | settings[option.Caption] = option.CurrentValue 55 | elif isinstance(option, Options.Nested): 56 | settings[option.Caption] = create_options_dict(option.Children) 57 | return settings 58 | 59 | options_dict = create_options_dict(mod.Options) 60 | 61 | if len(options_dict) > 0: 62 | mod_settings[_OPTIONS_CATEGORY_NAME] = options_dict 63 | 64 | keybinds_dict = {} 65 | for input in mod.Keybinds: 66 | if isinstance(input, KeybindManager.Keybind): 67 | if not input.IsRebindable: 68 | continue 69 | keybinds_dict[input.Name] = input.Key 70 | else: 71 | dh.PrintWarning(KeybindManager.Keybind._list_deprecation_warning) 72 | keybinds_dict[input[0]] = input[1] 73 | 74 | if len(keybinds_dict) > 0: 75 | mod_settings[_KEYBINDS_CATEGORY_NAME] = keybinds_dict 76 | 77 | if mod.SaveEnabledState != ModObjects.EnabledSaveType.NotSaved: 78 | mod_settings[_ENABLED_CATEGORY_NAME] = mod.IsEnabled 79 | 80 | if len(mod_settings.keys()) > 0: 81 | with open(GetSettingsFilePath(mod), "w") as file: 82 | json.dump(mod_settings, file, indent=4) 83 | 84 | 85 | def SaveAllModSettings() -> None: 86 | """ Saves the options, keybinds, and enabled state of all loaded mods, where applicable. """ 87 | for mod in ModObjects.Mods: 88 | try: 89 | SaveModSettings(mod) 90 | except Exception: 91 | unrealsdk.Log(f"Unable to save settings for '{mod.Name}'") 92 | tb = traceback.format_exc().split('\n') 93 | unrealsdk.Log(f" {tb[-4].strip()}") 94 | unrealsdk.Log(f" {tb[-3].strip()}") 95 | unrealsdk.Log(f" {tb[-2].strip()}") 96 | 97 | 98 | def LoadModSettings(mod: ModObjects.SDKMod) -> None: 99 | """ 100 | Loads the options, keybinds, and enabled state of a mod back from disk. 101 | 102 | Args: 103 | mod: The instance of the mod to load settings onto. 104 | """ 105 | settings: Dict[str, Any] 106 | try: 107 | with open(GetSettingsFilePath(mod)) as file: 108 | settings = json.load(file) 109 | except (FileNotFoundError, json.JSONDecodeError): 110 | return 111 | 112 | def load_options_dict(options: Sequence[Options.Base], settings: Dict[str, Any]) -> None: 113 | for option in options: 114 | if option.Caption not in settings: 115 | continue 116 | 117 | value = settings[option.Caption] 118 | 119 | if isinstance(option, Options.Boolean): 120 | if isinstance(value, str): 121 | if value in option.Choices: 122 | option.CurrentValue = bool(option.Choices.index(value)) 123 | elif value.lower() == "true": 124 | option.CurrentValue = True 125 | elif value.lower() == "false": 126 | option.CurrentValue = False 127 | else: 128 | option.CurrentValue = bool(value) 129 | elif isinstance(option, Options.Spinner): 130 | if value in option.Choices: 131 | option.CurrentValue = str(value) 132 | else: 133 | option.CurrentValue = option.StartingValue 134 | elif isinstance(option, Options.Slider): 135 | option.CurrentValue = max(option.MinValue, min(option.MaxValue, int(value))) 136 | elif isinstance(option, Options.Hidden): 137 | option.CurrentValue = value 138 | 139 | elif isinstance(option, Options.Nested): 140 | load_options_dict(option.Children, value) 141 | 142 | load_options_dict(mod.Options, settings.get(_OPTIONS_CATEGORY_NAME, {})) 143 | 144 | saved_keybinds = settings.get(_KEYBINDS_CATEGORY_NAME, {}) 145 | for input in mod.Keybinds: 146 | if isinstance(input, KeybindManager.Keybind): 147 | if input.Name in saved_keybinds: 148 | input.Key = saved_keybinds[input.Name] 149 | else: 150 | dh.PrintWarning(KeybindManager.Keybind._list_deprecation_warning) 151 | if input[0] in saved_keybinds: 152 | input[1] = saved_keybinds[input[0]] 153 | 154 | if settings.get(_ENABLED_CATEGORY_NAME, False): 155 | if mod.SaveEnabledState == ModObjects.EnabledSaveType.LoadWithSettings: 156 | if not mod.IsEnabled: 157 | mod.SettingsInputPressed("Enable") 158 | elif mod.SaveEnabledState == ModObjects.EnabledSaveType.LoadOnMainMenu: 159 | _mods_to_enable_on_main_menu.add(mod) 160 | 161 | 162 | def _FrontendGFxMovieStart(caller: unrealsdk.UObject, function: unrealsdk.UFunction, params: unrealsdk.FStruct) -> bool: 163 | """ 164 | This function is called upon reaching the main menu, after hotfix objects already exist and all 165 | the main packages are loaded. We use it to enable all `LoadOnMainMenu` mods. 166 | """ 167 | for mod in _mods_to_enable_on_main_menu: 168 | if not mod.IsEnabled: 169 | mod.SettingsInputPressed("Enable") 170 | 171 | _mods_to_enable_on_main_menu.clear() 172 | 173 | return True 174 | 175 | 176 | unrealsdk.RunHook("WillowGame.FrontendGFxMovie.Start", "ModMenu.SettingsManager", _FrontendGFxMovieStart) 177 | -------------------------------------------------------------------------------- /Mods/ModMenu/__init__.py: -------------------------------------------------------------------------------- 1 | import unrealsdk 2 | import sys 3 | from typing import Tuple 4 | 5 | __all__: Tuple[str, ...] = ( 6 | "AnyHook", 7 | "ClientMethod", 8 | "Deprecated", 9 | "EnabledSaveType", 10 | "Game", 11 | "GetOrderedModList", 12 | "GetSettingsFilePath", 13 | "Hook", 14 | "HookFunction", 15 | "HookMethod", 16 | "InputEvent", 17 | "Keybind", 18 | "KeybindCallback", 19 | "LoadModSettings", 20 | "ModPriorities", 21 | "Mods", 22 | "ModTypes", 23 | "NameChangeMsg", 24 | "Options", 25 | "PrintWarning", 26 | "RegisterHooks", 27 | "RegisterMod", 28 | "RegisterNetworkMethods", 29 | "RemoveHooks", 30 | "SaveAllModSettings", 31 | "SaveModSettings", 32 | "SDKMod", 33 | "ServerMethod", 34 | "UnregisterNetworkMethods", 35 | ) 36 | 37 | 38 | # Need to define these up here so that they're accessable when importing the other files 39 | VERSION_MAJOR = 2 40 | VERSION_MINOR = 5 41 | 42 | unrealsdk.Log(f"[ModMenu] Version: {VERSION_MAJOR}.{VERSION_MINOR}") 43 | 44 | from . import DeprecationHelper as dh # noqa: E402 45 | from . import OptionManager, Options, SettingsManager # noqa: E402 46 | from .DeprecationHelper import Deprecated, NameChangeMsg, PrintWarning # noqa: E402 47 | from .HookManager import (AnyHook, Hook, HookFunction, HookMethod, RegisterHooks, # noqa: E402 48 | RemoveHooks) 49 | from .KeybindManager import InputEvent, Keybind, KeybindCallback # noqa: E402 50 | from .MenuManager import GetOrderedModList # noqa: E402 51 | from .ModObjects import (EnabledSaveType, Game, ModPriorities, Mods, ModTypes, # noqa: E402 52 | RegisterMod, SDKMod) 53 | from .NetworkManager import (ClientMethod, RegisterNetworkMethods, ServerMethod, # noqa: E402 54 | UnregisterNetworkMethods) 55 | from .SettingsManager import (GetSettingsFilePath, LoadModSettings, # noqa: E402 56 | SaveAllModSettings, SaveModSettings) 57 | 58 | from . import ModObjects # noqa: E402 # isort: skip # Avoid circular import 59 | 60 | """ 61 | From this point on this file defines just aliases, most of which should be considered deprecated. 62 | 63 | 64 | When enabling a mod, the default `SettingsInputPressed` calls `ModOptionChanged` on every option. 65 | This behaviour should be considered deprecated, move it into your `Enable` if you need it. 66 | Unfortuantly there's no easy way to detect mods that rely on this to print a warning, and it's not 67 | something that can be aliased, so this text warning will have to do. 68 | """ 69 | 70 | 71 | sys.modules["bl2sdk"] = unrealsdk 72 | sys.modules["Mods.ModManager"] = ModObjects 73 | sys.modules["Mods.OptionManager"] = OptionManager 74 | sys.modules["Mods.SaveManager"] = SettingsManager 75 | 76 | unrealsdk.PythonManagerVersion = VERSION_MAJOR 77 | 78 | ModObjects.BL2MOD = ModObjects.SDKMod # type: ignore 79 | unrealsdk.BL2MOD = ModObjects.SDKMod 80 | 81 | unrealsdk.Mods = ModObjects.Mods 82 | unrealsdk.ModTypes = ModObjects.ModTypes 83 | unrealsdk.RegisterMod = ModObjects.RegisterMod 84 | 85 | 86 | OptionManager.Options = Options 87 | unrealsdk.Options = Options 88 | 89 | # When removing this, also make sure to edit `Spinner.__init__()` 90 | _msg = dh.NameChangeMsg("Spinner.StartingChoice", "Spinner.StartingValue") 91 | Options.Spinner.StartingChoice = property( # type: ignore 92 | dh.Deprecated(_msg, lambda self: self.StartingValue), 93 | dh.Deprecated(_msg, lambda self, val: self.__setattr__("StartingValue", val)) 94 | ) 95 | _msg = dh.NameChangeMsg("Boolean.StartingChoiceIndex", "Boolean.StartingValue") 96 | Options.Boolean.StartingChoiceIndex = property( # type: ignore 97 | dh.Deprecated(_msg, lambda self: self.StartingValue), 98 | dh.Deprecated(_msg, lambda self, val: self.__setattr__("StartingValue", val)) 99 | ) 100 | del _msg 101 | 102 | 103 | SettingsManager.storeModSettings = SettingsManager.SaveAllModSettings # type: ignore 104 | storeModSettings = SettingsManager.SaveAllModSettings # noqa: N816 105 | -------------------------------------------------------------------------------- /Mods/Quickload/__init__.py: -------------------------------------------------------------------------------- 1 | import unrealsdk 2 | from unrealsdk import * 3 | from Mods import ModMenu 4 | from Mods.ModMenu import EnabledSaveType, Mods, ModTypes, Options, RegisterMod, SDKMod, Hook 5 | from typing import List 6 | 7 | 8 | _DefaultGameInfo = UObject.FindObjectsContaining("WillowCoopGameInfo WillowGame.Default__WillowCoopGameInfo")[0] 9 | 10 | 11 | def _DisplayFeedback(message, time=2.0) -> None: 12 | playerController = GetEngine().GamePlayers[0].Actor 13 | HUDMovie = playerController.GetHUDMovie() 14 | if HUDMovie is None: 15 | return 16 | duration = time * _DefaultGameInfo.GameSpeed 17 | HUDMovie.ClearTrainingText() 18 | HUDMovie.AddTrainingText( 19 | message, "Map Loader", duration, (), "", False, 0, playerController.PlayerReplicationInfo, True 20 | ) 21 | 22 | 23 | def _StoreLocation() -> None: 24 | PC = GetEngine().GamePlayers[0].Actor 25 | locale = PC.Pawn.Location 26 | _Position = [locale.X, locale.Y, locale.Z] 27 | _ModInstance.Coords[0] = locale.X 28 | _ModInstance.Coords[1] = locale.Y 29 | _ModInstance.Coords[2] = locale.Z 30 | rotate = PC.Rotation 31 | _ModInstance.Rotation[0] = rotate.Pitch 32 | _ModInstance.Rotation[1] = rotate.Roll 33 | _ModInstance.Rotation[2] = rotate.Yaw 34 | Log(f"[Map Loader] Storing location: ({_ModInstance.Coords}), ({_ModInstance.Rotation})") 35 | 36 | 37 | def _RestoreLocation() -> None: 38 | PC = GetEngine().GamePlayers[0].Actor 39 | # Restore our location. 40 | PC.Pawn.Location.X = _ModInstance.Coords[0] 41 | PC.Pawn.Location.Y = _ModInstance.Coords[1] 42 | PC.Pawn.Location.Z = _ModInstance.Coords[2] 43 | Log(f"[Map Loader] Restoring location: ({_ModInstance.Coords}), ({_ModInstance.Rotation})") 44 | rotate = PC.Rotation 45 | rotate.Pitch = _ModInstance.Rotation[0] 46 | rotate.Roll = _ModInstance.Rotation[1] 47 | rotate.Yaw = _ModInstance.Rotation[2] 48 | 49 | 50 | def _ReloadCurrentMap(skipSave): 51 | PC = GetEngine().GamePlayers[0].Actor 52 | if _ModInstance.toggledLocation: 53 | if _ModInstance.consistentLocation: 54 | _StoreLocation() 55 | else: 56 | if not _ModInstance.consistentLocation: 57 | _StoreLocation() 58 | 59 | _ModInstance.toggledLocation = False 60 | # Our currently selected difficulty for the main menu 61 | _ModInstance.SelectedDifficulty = PC.GetCurrentPlaythrough() 62 | # Get our current save game we'll need it for the OP levels 63 | wsg = PC.GetCachedSaveGame() 64 | 65 | # Our current OP level if we need it, game is weird 66 | if wsg.LastOverpowerChoice and wsg.NumOverpowerLevelsUnlocked: 67 | _ModInstance.OverpoweredLevel = max(min(wsg.LastOverpowerChoice, wsg.NumOverpowerLevelsUnlocked), 0) 68 | else: 69 | _ModInstance.OverpoweredLevel = -1 70 | 71 | # Load Map 72 | _ModInstance.loading = True 73 | # This is the function that BL2 uses for save quits. 74 | # ReturnToTitleScreen(optional bool bSkipSave, optional bool bRemoveSplitPlayer) 75 | # (In the SDK, optional arguments aren't optional) 76 | 77 | PC.ReturnToTitleScreen(skipSave, False) 78 | 79 | 80 | @Hook("WillowGame.WillowHUD.CreateWeaponScopeMovie") 81 | def _MapLoadHook(caller: UObject, function: UFunction, params: FStruct) -> None: 82 | if _ModInstance.restoreLocation and _ModInstance.loading: 83 | pc = GetEngine().GamePlayers[0].Actor 84 | HUDMovie = pc.myHUD.HUDMovie 85 | # PC is sometimes none when the hooked function is called, this means that the hook is running to early. 86 | # Same thing with the HUDMovie. 87 | if pc.Pawn is None or HUDMovie is None: 88 | return True 89 | _RestoreLocation() 90 | _DisplayFeedback("Farming Location Restored", 3.0) 91 | _ModInstance.loading = False 92 | return True 93 | 94 | 95 | # This is how we know that we're in the main menu. Its slightly janky, but it works. 96 | @Hook("WillowGame.FrontendGFxMovie.OnTick") 97 | def _MainMenuHook(caller: UObject, function: UFunction, params: FStruct) -> None: 98 | try: 99 | if _ModInstance.loading == True: 100 | PC = GetEngine().GamePlayers[0].Actor 101 | # We'll need this to reload to the current difficulty. 102 | gfx = UObject.FindObjectsContaining("FrontendGFxMovie ")[1] 103 | if gfx is None or PC is None: 104 | return True 105 | # This is how the game knows what OP level we're on. 106 | if _ModInstance.OverpoweredLevel != -1: 107 | PC.OnSelectOverpowerLevel(PC.GetCachedSaveGame(), _ModInstance.OverpoweredLevel) 108 | # I don't *think* this does anything, might want to do it just in case. Weird Game. 109 | gfx.CurrentSelectedOverpowerLevel = _ModInstance.OverpoweredLevel 110 | Log(f"[Map Loader] Loading WSG @ {_ModInstance.SelectedDifficulty}, OP{_ModInstance.OverpoweredLevel}") 111 | # Here we reload our save, like how the `Continue` button does. 112 | gfx.LaunchSaveGame(_ModInstance.SelectedDifficulty) 113 | except: 114 | pass 115 | return True 116 | 117 | 118 | class MapLoader(SDKMod): 119 | Name: str = "Borderlands 2 Map Reloader" 120 | Version: str = "1.1" 121 | Author: str = "FromDarkHell" 122 | Description: str = "Quickly farm items and save quit at a button press!\n\nLocation Restore: Whether to restore location on quickload" 123 | Types: ModTypes = ModTypes.Utility 124 | SaveEnabledState: EnabledSaveType = EnabledSaveType.LoadWithSettings 125 | 126 | Keybinds: List[ModMenu.Keybind] = [ 127 | ModMenu.Keybind("Quickload w/o Saving", "F7"), 128 | ModMenu.Keybind("Quickload w/ Saving", "F8"), 129 | ModMenu.Keybind("Toggle Location Restore", "F10"), 130 | ModMenu.Keybind("Save Location", "F5"), 131 | ] 132 | 133 | def __init__(self): 134 | # It might be a good idea to restore our position after a load. 135 | self.restoreLocation = True 136 | self.loading = False 137 | self.consistentLocation = False 138 | self.toggledLocation = False 139 | # Store some data that we can use to reload the map 140 | self.SelectedDifficulty = 0 141 | self.OverpoweredLevel = 0 142 | self.Coords = [0, 0, 0] # X, Y, Z 143 | self.Rotation = [0, 0, 0] # Pitch, Roll, Yaw 144 | 145 | def GameInputPressed(self, input) -> None: 146 | name = input.Name 147 | if name == "Quickload w/o Saving": 148 | _ReloadCurrentMap(True) 149 | elif name == "Quickload w/ Saving": 150 | _ReloadCurrentMap(False) 151 | elif name == "Toggle Location Restore": 152 | self.restoreLocation = not self.restoreLocation 153 | state = "Location restoration is now {}".format("enabled" if self.restoreLocation else "disabled") 154 | Log(f"[Map Loader] {state}") 155 | _DisplayFeedback(state) 156 | elif name == "Save Location": 157 | self.toggledLocation = True 158 | self.consistentLocation = not self.consistentLocation 159 | state = "Save Location is now {}".format( 160 | "enabled (Saves on quickload quit)" if self.consistentLocation else "disabled" 161 | ) 162 | Log(f"[Map Loader] {state}") 163 | _DisplayFeedback(state) 164 | 165 | def Enable(self) -> None: 166 | super().Enable() 167 | 168 | def Disable(self) -> None: 169 | ModMenu.RemoveHooks(self) 170 | 171 | 172 | _ModInstance = MapLoader() 173 | RegisterMod(_ModInstance) 174 | -------------------------------------------------------------------------------- /Mods/ReadOnly/__init__.py: -------------------------------------------------------------------------------- 1 | from unrealsdk import * 2 | from ..ModManager import BL2MOD, RegisterMod 3 | import math 4 | 5 | class ReadOnly(BL2MOD): 6 | Name = "Borderlands Easy Read Only" 7 | Description = "Toggle Read Only on a button press" 8 | readOnly = False 9 | toggledReadOnly = False 10 | 11 | DefaultGameInfo = UObject.FindObjectsContaining("WillowCoopGameInfo WillowGame.Default__WillowCoopGameInfo")[0] 12 | Keybinds = [["Toggle Read Only", "F2"]] 13 | 14 | def displayFeedback(self): 15 | PC = GetEngine().GamePlayers[0].Actor 16 | HUDMovie = PC.myHUD.HUDMovie 17 | try: 18 | if PC is None or HUDMovie is None: 19 | return True 20 | if self.readOnly: 21 | HUDMovie.AddTrainingText("Read Only: Enabled", "Read Only", math.inf, (), "", False, 0, PC.PlayerReplicationInfo, True, 0, 0) 22 | elif self.toggledReadOnly: 23 | self.toggledReadOnly = False 24 | HUDMovie.ClearTrainingText() 25 | except: 26 | return True 27 | return True 28 | 29 | def GameInputPressed(self, input): 30 | if input.Name == "Toggle Read Only": 31 | self.toggledReadOnly = True 32 | self.readOnly = not self.readOnly 33 | self.displayFeedback() 34 | 35 | def Enable(self): 36 | 37 | def hookCanSaveGame(caller: UObject, function: UFunction, params: FStruct) -> bool: 38 | if self.readOnly: 39 | return False 40 | return True 41 | 42 | def hookTrainingText(caller: UObject, function: UFunction, params: FStruct): 43 | self.displayFeedback() 44 | return True 45 | 46 | RegisterHook("WillowGame.WillowPlayerController.CanSaveGame", "HookSaveGame", hookCanSaveGame) 47 | RegisterHook("WillowGame.WillowHUDGFxMovie.DrawTrainingText", "HookTrainingText", hookTrainingText) 48 | RegisterHook("WillowGame.WillowHUD.CreateWeaponScopeMovie", "HookTrainingText", hookTrainingText) 49 | 50 | def Disable(self): 51 | RemoveHook("WillowGame.WillowPlayerController.CanSaveGame", "HookSaveGame") 52 | RemoveHook("WillowGame.WillowHUDGFxMovie.DrawTrainingText", "HookTrainingText") 53 | RemoveHook("WillowGame.WillowHUD.CreateWeaponScopeMovie", "HookTrainingText") 54 | 55 | RegisterMod(ReadOnly()) -------------------------------------------------------------------------------- /Mods/__init__.py: -------------------------------------------------------------------------------- 1 | import unrealsdk 2 | import importlib 3 | import os 4 | import traceback 5 | 6 | # Need to make sure this is all loaded and aliased up before loading any mods 7 | from Mods import ModMenu # noqa: F401, E402 8 | 9 | _full_traceback = False 10 | 11 | for name in os.listdir(os.path.dirname(__file__)): 12 | absolute_path = os.path.join(os.path.dirname(__file__), name) 13 | if not os.path.isdir(absolute_path): 14 | continue 15 | 16 | # Temporarily filter out `General` incase people forget to delete it 17 | if name.startswith(".") or name in ("__pycache__", "ModMenu", "General"): 18 | continue 19 | 20 | try: 21 | importlib.import_module(f".{name}", "Mods") 22 | except Exception: 23 | unrealsdk.Log(f"Failed to import mod: {name}") 24 | tb = traceback.format_exc().split('\n') 25 | if _full_traceback: 26 | for line in tb: 27 | unrealsdk.Log(line) 28 | else: 29 | unrealsdk.Log(f" {tb[-4].strip()}") 30 | unrealsdk.Log(f" {tb[-3].strip()}") 31 | unrealsdk.Log(f" {tb[-2].strip()}") 32 | -------------------------------------------------------------------------------- /Mods/linters.txt: -------------------------------------------------------------------------------- 1 | flake8-bugbear==20.11.1 2 | flake8-comprehensions==3.3.1 3 | flake8-isort==4.0.0 4 | flake8-mutable==1.2.0 5 | flake8-noqa==1.1.0 6 | flake8-pyi==20.10.0 7 | flake8-simplify==0.13.0 8 | flake8==3.8.4 9 | isort==5.7.0 10 | mypy==0.800 11 | pep8-naming==0.11.1 12 | rope==0.18.0 13 | -------------------------------------------------------------------------------- /Mods/setup.cfg: -------------------------------------------------------------------------------- 1 | [mypy] 2 | python_version = 3.7 3 | strict = True 4 | 5 | [mypy-unrealsdk] 6 | ignore_missing_imports = True 7 | 8 | [flake8] 9 | python_version = 3.7 10 | ignore = Y011, SIM105, SIM106, E501, W503, N802, N803, N813, B901, B950 11 | max-line-length = 100 12 | ignore-names = PC,PCs,PRI,PRIs 13 | exclude = .env 14 | noqa-require-code = True 15 | 16 | [isort] 17 | py_version = 37 18 | line_length = 100 19 | known_unrealsdk = unrealsdk 20 | known_modmenu = Mods.ModMenu 21 | sections = FUTURE, UNREALSDK, STDLIB, THIRDPARTY, MODMENU, FIRSTPARTY, LOCALFOLDER 22 | no_lines_before = STDLIB, FIRSTPARTY 23 | -------------------------------------------------------------------------------- /PythonSDK.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.27428.2037 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Borderlands 2 SDK", "PythonSDK.vcxproj", "{32794345-2BB2-45E0-A461-914D13B0279F}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|x86 = Debug|x86 11 | Release|x86 = Release|x86 12 | EndGlobalSection 13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 14 | {32794345-2BB2-45E0-A461-914D13B0279F}.Debug|x86.ActiveCfg = Debug|Win32 15 | {32794345-2BB2-45E0-A461-914D13B0279F}.Debug|x86.Build.0 = Debug|Win32 16 | {32794345-2BB2-45E0-A461-914D13B0279F}.Release|x86.ActiveCfg = Release|Win32 17 | {32794345-2BB2-45E0-A461-914D13B0279F}.Release|x86.Build.0 = Release|Win32 18 | EndGlobalSection 19 | GlobalSection(SolutionProperties) = preSolution 20 | HideSolutionNode = FALSE 21 | EndGlobalSection 22 | GlobalSection(ExtensibilityGlobals) = postSolution 23 | SolutionGuid = {0F5A3673-B782-4AAF-B0EA-528EA4892494} 24 | EndGlobalSection 25 | EndGlobal 26 | -------------------------------------------------------------------------------- /PythonSDK.sln.DotSettings.user: -------------------------------------------------------------------------------- 1 |  2 | True 3 | True -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | This repository has been archived. 2 | 3 | If you're looking for the [BL2/TPS/AoDK Mod Manager, see here](https://github.com/bl-sdk/willow2-mod-manager/) 4 | 5 | If you were looking for the code, [bl-sdk/unrealsdk](https://github.com/bl-sdk/unrealsdk) and 6 | [bl-sdk/pyunrealsdk](https://github.com/bl-sdk/pyunrealsdk) and are the successors to this repo. 7 | 8 | 9 | # UnrealEngine PythonSDK 10 | [![Support Discord](https://img.shields.io/static/v1?label=&message=Support%20Discord&logo=discord&color=424)](https://discord.gg/bXeqV8Ef9R) 11 | [![Developer Discord](https://img.shields.io/static/v1?label=&message=Developer%20Discord&logo=discord&color=222)](https://discord.gg/VJXtHvh) 12 | 13 | An UnrealEngine Plugin enabling using Python to write plugins that interact directly with UE objects 14 | 15 | ## List of confirmed compatible games 16 | - Borderlands 2 17 | - Borderlands: The Pre-Sequel 18 | - Tiny Tina’s Assault on Dragon Keep: A Wonderlands One-shot Adventure 19 | -------------------------------------------------------------------------------- /scripts/add_init.py: -------------------------------------------------------------------------------- 1 | from os import listdir 2 | 3 | sdk_dir = 'C:\\Users\\abahb\\source\\repos\\BL2-SDK\\bl2-sdk\\pydefs\\' 4 | 5 | relationships = {} 6 | 7 | template = '\t\t.def(py::init<>())\n' 8 | 9 | for filename in listdir(sdk_dir): 10 | if '_structs' in filename: 11 | lines = [] 12 | with open(sdk_dir + filename) as f: 13 | for line in f.readlines(): 14 | lines.append(line) 15 | if 'py::class_' in line: 16 | lines.append(template) 17 | 18 | with open(sdk_dir + filename, 'w') as f: 19 | for line in lines: 20 | f.write(line) -------------------------------------------------------------------------------- /scripts/add_static.py: -------------------------------------------------------------------------------- 1 | from os import listdir 2 | 3 | sdk_dir = 'C:\\Users\\abahb\\source\\repos\\BL2-SDK\\bl2-sdk\\pydefs\\' 4 | 5 | relationships = {} 6 | 7 | template = ' .def_static("StaticClass", &{}::StaticClass, py::return_value_policy::reference)\n' 8 | 9 | for filename in listdir(sdk_dir): 10 | if 'Class' in filename: 11 | lines = [] 12 | with open(sdk_dir + filename) as f: 13 | for line in f.readlines(): 14 | lines.append(line) 15 | if 'py::class_' in line: 16 | obj_def = line.split('<')[1].split('>')[0].strip() 17 | obj = obj_def.split(',')[0] 18 | lines.append(template.format(obj)) 19 | 20 | with open(sdk_dir + filename, 'w') as f: 21 | for line in lines: 22 | f.write(line) -------------------------------------------------------------------------------- /scripts/distribute_tarrays.py: -------------------------------------------------------------------------------- 1 | import os 2 | import re 3 | 4 | modules = ['Core', 5 | 'Engine', 6 | 'GameFramework', 7 | 'GFxUI', 8 | 'GearboxFramework', 9 | 'WillowGame', 10 | 'AkAudio', 11 | 'IpDrv', 12 | 'WinDrv', 13 | 'XAudio2', 14 | 'OnlineSubsystemSteamworks', 15 | 'Base'] 16 | 17 | dir_path_structs = 'C:/Program Files (x86)/Steam/steamapps/common/Borderlands 2/Binaries/plugins/python/include/sdk/structs/' 18 | dir_path_classes = 'C:/Program Files (x86)/Steam/steamapps/common/Borderlands 2/Binaries/plugins/python/include/sdk/classes/' 19 | tarrays_file = 'C:/Program Files (x86)/Steam/steamapps/common/Borderlands 2/Binaries/plugins/python/include/sdk/TArrayTypes.py' 20 | 21 | tarrays = {} 22 | 23 | header = "._fields_ = " 24 | template = """class {}(Structure):\n\ 25 | pass\n\n""" 26 | 27 | with open(tarrays_file) as f: 28 | lines = f.readlines() 29 | for index, line in enumerate(lines): 30 | if header in line: 31 | tarray_type = lines[index + 1].split('(')[-1].split(')')[0] 32 | tarrays[tarray_type] = 'class ' + ''.join(lines[index : index + 5]).replace('._fields_','(Structure)\n _fields_') 33 | 34 | for module in modules: 35 | lines = [] 36 | found = [] 37 | top = [] 38 | done = False 39 | with open(dir_path_structs + module + '.py') as f: 40 | after = None 41 | for line in f.readlines(): 42 | lines.append(line) 43 | if 'class ' in line: 44 | defining = line.split(' ')[1].split('(')[0] 45 | if defining in tarrays.keys(): 46 | found.append(defining) 47 | after = defining 48 | if line == '\n' and after: 49 | lines.append(tarrays[after]) 50 | after = None 51 | with open(dir_path_structs + module + '.py', 'w') as f: 52 | for l in lines: 53 | f.write(l) -------------------------------------------------------------------------------- /scripts/fix_classes.py: -------------------------------------------------------------------------------- 1 | import re 2 | 3 | 4 | function_regex = re.compile(r'\[(\d+)\] Class (\w+)\.(\w+).*') 5 | functions = {} 6 | with open('C:\\SDK_GEN\\BL2\\ObjectsDump.txt') as f: 7 | lookahead = None 8 | for line in f.readlines(): 9 | if '] Class ' in line: 10 | matches = function_regex.match(line) 11 | if matches: 12 | id, module, clas = matches.groups() 13 | if module not in functions.keys(): 14 | functions[module] = {} 15 | functions[module][clas] = id 16 | 17 | 18 | sdk_dir = 'C:\\Users\\abahb\\source\\repos\\BL2-SDK\\bl2-sdk\\' 19 | for module in functions.keys(): 20 | lines = [] 21 | with open(sdk_dir + module + '_classes.h') as f: 22 | new_id = None 23 | for line in f.readlines(): 24 | if line.startswith('class '): 25 | clas = line.split(' ')[1][1:].strip() 26 | if clas in functions[module].keys(): 27 | new_id = functions[module][clas] 28 | if 'UObject::GObjObjects()->Data' in line: 29 | old_id = line.split('[')[1].split(']')[0] 30 | line = line.replace(old_id, str(int(new_id))) 31 | lines.append(line) 32 | with open(sdk_dir + module + '_classes.h', 'w') as f: 33 | for line in lines: 34 | f.write(line) -------------------------------------------------------------------------------- /scripts/fix_fuckup.py: -------------------------------------------------------------------------------- 1 | import os 2 | import re 3 | 4 | modules = ['Core', 5 | 'Engine', 6 | 'GameFramework', 7 | 'GFxUI', 8 | 'GearboxFramework', 9 | 'WillowGame', 10 | 'AkAudio', 11 | 'IpDrv', 12 | 'WinDrv', 13 | 'XAudio2', 14 | 'OnlineSubsystemSteamworks'] 15 | 16 | dir_path_lua = 'C:/Program Files (x86)/Steam/steamapps/common/Borderlands 2/Binaries/plugins/include/sdk/classes/' 17 | dir_path_python = 'C:/Program Files (x86)/Steam/steamapps/common/Borderlands 2/Binaries/plugins/python/include/sdk/classes/' 18 | used = set() 19 | defined = set() 20 | 21 | used_regex = re.compile(r'.* TArray_(/w+).*') 22 | defined_regex = re.compile(r'.*struct TArray_(/w+) {') 23 | 24 | for module in modules: 25 | lens = [] 26 | with open(dir_path_lua + module + '.lua') as f: 27 | for line in f.readlines(): 28 | if '[0x' in line: 29 | lens += [line.split('0x')[1].split(']')[0]] 30 | i = 0 31 | lines = [] 32 | with open(dir_path_python + module + '.py') as f: 33 | for line in f.readlines(): 34 | if '0x' in line: 35 | if '0x)' in line: 36 | print(line) 37 | line = line.replace('0x)', '0x{})'.format(lens[i])) 38 | print(line) 39 | i = i + 1 40 | lines.append(line) 41 | with open(dir_path_python + module + '.py', 'w') as f: 42 | for line in lines: 43 | f.write(line) -------------------------------------------------------------------------------- /scripts/fix_funcs.py: -------------------------------------------------------------------------------- 1 | import re 2 | 3 | 4 | function_regex = re.compile(r'\[(\d+)\] Function (\w+)\.(\w+)\.(\w+).*') 5 | property_regex = re.compile(r'\[(\d+)\] \w+ (\w+)\.(\w+)\.(\w+)\.(\w+).*') 6 | functions = {} 7 | with open('C:\\SDK_GEN\\BL2\\ObjectsDump.txt') as f: 8 | lookahead = None 9 | for line in f.readlines(): 10 | if '] Function ' in line: 11 | matches = function_regex.match(line) 12 | if matches: 13 | id, module, clas, func = matches.groups() 14 | if module not in functions.keys(): 15 | functions[module] = {} 16 | functions[module]['{}.{}'.format(clas, func)] = {'id': id, 'properties': {}} 17 | elif 'Property ' in line: 18 | matches = property_regex.match(line) 19 | if matches: 20 | id, module, clas, func, prop = matches.groups() 21 | if module in functions.keys() and '{}.{}'.format(clas, func) in functions[module].keys(): 22 | functions[module]['{}.{}'.format(clas, func)]['properties'][prop] = id 23 | 24 | 25 | sdk_dir = 'C:\\Users\\abahb\\source\\repos\\BL2-SDK\\bl2-sdk\\' 26 | for module in functions.keys(): 27 | lines = [] 28 | with open(sdk_dir + module + '_functions.cpp') as f: 29 | new_id = None 30 | for line in f.readlines(): 31 | if line[0] != '\t' and '::' in line: 32 | clas = line.split('::')[0].split(' ')[-1].strip()[1:] 33 | if 'pFn' in line and 'UObject::GObjObjects()->Data' in line: 34 | func = clas + '.' + line.split('pFn')[1].split(' ')[0] 35 | if func in functions[module].keys(): 36 | new_id = functions[module][func] 37 | old_id = line.split('[')[1].split(']')[0] 38 | print(new_id) 39 | line = line.replace(old_id, str(int(new_id['id']))) 40 | lines.append(line) 41 | with open(sdk_dir + module + '_functions.cpp', 'w') as f: 42 | for line in lines: 43 | f.write(line) -------------------------------------------------------------------------------- /scripts/fix_pointers.py: -------------------------------------------------------------------------------- 1 | import os 2 | import re 3 | 4 | files = ['Core_classes.h', 5 | 'Engine_classes.h', 6 | 'GameFramework_classes.h', 7 | 'GFxUI_classes.h', 8 | 'GearboxFramework_classes.h', 9 | 'WillowGame_classes.h', 10 | 'AkAudio_classes.h', 11 | 'IpDrv_classes.h', 12 | 'WinDrv_classes.h', 13 | 'XAudio2_classes.h', 14 | 'OnlineSubsystemSteamworks_classes.h'] 15 | import os 16 | 17 | dir_path_python = 'C:/Users/abahb/source/repos/BL2-SDK/bl2-sdk/' 18 | 19 | template = 'UClass* {}::pClassPointer = NULL;' 20 | 21 | funcs = {} 22 | for clas in files: 23 | os.system('cls') 24 | print(clas) 25 | with open(dir_path_python + clas) as f: 26 | for line in f.readlines(): 27 | if line.startswith('class '): 28 | print(template.format(line.split(' ')[1])) 29 | input("Press Enter to continue...") -------------------------------------------------------------------------------- /scripts/fix_references.py: -------------------------------------------------------------------------------- 1 | import os 2 | import re 3 | 4 | files = ['Core_classes.h', 5 | 'Engine_classes.h', 6 | 'GameFramework_classes.h', 7 | 'GFxUI_classes.h', 8 | 'GearboxFramework_classes.h', 9 | 'WillowGame_classes.h', 10 | 'AkAudio_classes.h', 11 | 'IpDrv_classes.h', 12 | 'WinDrv_classes.h', 13 | 'XAudio2_classes.h', 14 | 'OnlineSubsystemSteamworks_classes.h'] 15 | 16 | classes = ["AActor", 17 | "ACamera", 18 | "AController", 19 | "ADualWieldActionSkill", 20 | "AGameInfo", 21 | "AGearboxMind", 22 | "AInventoryManager", 23 | "ALiftActionSkill", 24 | "AMissionTracker", 25 | "APawn", 26 | "APlayerController", 27 | "AResourcePoolManager", 28 | "AStatusEffectProxyActor", 29 | "AVehicle", 30 | "AWillowCoopGameInfo", 31 | "AWillowEquipAbleItem", 32 | "AWillowInteractiveObject", 33 | "AWillowInventory", 34 | "AWillowInventoryManager", 35 | "AWillowItem", 36 | "AWillowPawn", 37 | "AWillowPlayerController", 38 | "AWillowPlayerPawn", 39 | "AWillowReplicatedEmitter", 40 | "AWillowShield", 41 | "AWillowTurretWeapon", 42 | "AWillowVehicle", 43 | "AWillowVehicleBase", 44 | "AWillowVehicleWeapon", 45 | "AWillowVendingMachineBlackMarket", 46 | "AWillowWeapon", 47 | "AWillowWeaponPawn", 48 | "AWorldInfo", 49 | "UActionSequence", 50 | "UAIFactoryBase", 51 | "UAnimSequence", 52 | "UAssetLibraryManager", 53 | "UAttributeDefinition", 54 | "UBehaviorBase", 55 | "UBehaviorKernel", 56 | "UBodyClassDefinition", 57 | "UCameraModifierLookAt", 58 | "UChassisDefinition", 59 | "UCustomizationGFxMovie", 60 | "UCylinderComponent", 61 | "UFaceFXAsset", 62 | "UForceFeedbackWaveform", 63 | "UGearboxAIFactory", 64 | "UGearboxCoverStateManager", 65 | "UGearboxDialogComponent", 66 | "UIHitRegionInfoProvider", 67 | "UInteractiveObjectDefinition", 68 | "UIParameterBehavior", 69 | "UIStatusEffectTarget", 70 | "UMaterialInstanceConstant", 71 | "UMaterialInterface", 72 | "UMeshComponent", 73 | "UNavigationHandle", 74 | "UParticleSystemComponent", 75 | "UPhysicalMaterial", 76 | "UPlayerSkillTree", 77 | "UPrimitiveComponent", 78 | "USeqAct_Toggle", 79 | "USequenceAction", 80 | "USequenceOp", 81 | "USkeletalMeshComponent", 82 | "UStaticMeshComponent", 83 | "UUIDataStore_OnlinePlaylists", 84 | "UUIResourceCombinationProvider", 85 | "UVehicleSpawnStationGFxMovie", 86 | "UWillowGlobals"] 87 | 88 | dir_path_python = 'C:/Users/abahb/source/repos/BL2-SDK/bl2-sdk/' 89 | 90 | class Function_def(): 91 | def __init__(self, return_type, name, params, pointers): 92 | self.return_type = return_type 93 | self.name = name 94 | self.params = params 95 | self.pointers = pointers 96 | 97 | class Pointer(): 98 | def __init__(self, pre, t, name): 99 | self.pre = pre 100 | self.type = t 101 | self.name = name 102 | 103 | 104 | 105 | template = '[]({class_name} &self {py_args}) {{ {init} {capture} self.{function_name}({new_args}); return py::make_tuple({returned_args}); }})\n' 106 | 107 | def generate_lambda(fun): 108 | py_args = fun.params 109 | init = "" 110 | replaced = [] 111 | for pointer in fun.pointers: 112 | replaced.append(pointer.name) 113 | if pointer.pre: 114 | pointer_arg = "{} {} py{}".format(pointer.pre, pointer.type, pointer.name) 115 | init += pointer_arg + " = 0 ; " 116 | else: 117 | pointer_arg = "{} py{}".format(pointer.type, pointer.name) 118 | if pointer.type == 'char': 119 | init += pointer_arg + " = malloc(sizeof(char) * 0xFF) ; " 120 | else: 121 | init += pointer_arg + " = ({0})malloc(sizeof({1})) ; ".format(pointer.type, pointer.type[:-1]) 122 | py_args = py_args.replace(pointer_arg.replace(" py", " "), "") 123 | py_args = py_args.strip() 124 | py_args = py_args.replace(', ,', ',') 125 | py_args = py_args.replace(', ,', ',') 126 | py_args = py_args.replace(', ,', ',') 127 | if py_args.startswith(','): 128 | py_args = py_args[1:] 129 | if py_args.endswith(','): 130 | py_args = py_args[:-1] 131 | py_args = py_args.strip() 132 | if py_args: 133 | py_args = ', ' + py_args 134 | new_args = ', '.join([parm.split(' ')[-1] if parm.split(' ')[-1] not in replaced else "py" + parm.split(' ')[-1] for parm in fun.params.split(', ')]) 135 | returned_args = ', '.join(["*py" + parm.name for parm in fun.pointers]) 136 | capture = "" 137 | if fun.return_type != "void": 138 | capture = '{} ret = '.format(fun.return_type) 139 | if returned_args: 140 | returned_args = 'ret, ' + returned_args 141 | else: 142 | returned_args = 'ret' 143 | 144 | return(template.format(class_name = clas, py_args = py_args, init = init, capture = capture, function_name = fun.name, new_args = new_args, returned_args = returned_args)) 145 | 146 | 147 | funcs = {} 148 | for clas in files: 149 | print(clas) 150 | with open(dir_path_python + clas) as f: 151 | class_name = None 152 | for line in f.readlines(): 153 | if line.startswith("class "): 154 | class_name = line.split(" ")[1].strip() 155 | funcs[class_name] = {} 156 | if class_name not in classes: 157 | continue 158 | if ');' in line and '*' in line: 159 | start, end = line.split('(') 160 | function_name = start.split(' ')[-1] 161 | return_type = ' '.join(start.split(' ')[:-1]).strip() 162 | params = line.split('(')[-1].split(')')[0] 163 | pointers = [] 164 | for param in params.split(', '): 165 | if 'TArray' in param: 166 | continue 167 | if (param.startswith('struct ') or param.startswith('class ')) and '**' in param: 168 | s = param.split(' ') 169 | pointers.append(Pointer(s[0], s[1], s[2])) 170 | elif not param.startswith('struct ') and not param.startswith('class ') and '*' in param: 171 | s = param.split(' ') 172 | pointers.append(Pointer(None, ' '.join(s[:-1]), s[-1])) 173 | if pointers: 174 | funcs[class_name][function_name] = Function_def(return_type, function_name, params, pointers) 175 | 176 | pydefs = 'C:/Users/abahb/source/repos/BL2-SDK/bl2-sdk/pydefs/' 177 | 178 | for clas in funcs.keys(): 179 | if not funcs[clas]: 180 | continue 181 | with open(pydefs + '_Classes_{}.cpp'.format(clas)) as f: 182 | lines = [] 183 | for line in f.readlines(): 184 | if '.def(' in line: 185 | function_name = line.split('"')[1] 186 | if function_name in funcs[clas].keys(): 187 | lines += line.split(',')[0] + ', ' + generate_lambda(funcs[clas][function_name]) 188 | continue 189 | if '.staticmethod' in line: 190 | continue 191 | lines += line 192 | with open(pydefs + '_Classes_{}.cpp'.format(clas), 'w') as f: 193 | for line in lines: 194 | f.write(line) -------------------------------------------------------------------------------- /scripts/gen_funcs.py: -------------------------------------------------------------------------------- 1 | import os 2 | import re 3 | 4 | dir_path = os.path.dirname(os.path.realpath(__file__)) 5 | funcs = {} 6 | 7 | regex = re.compile(r'^.* (\w+)::(\w+).*\)$') 8 | 9 | for root, dirs, files in os.walk(dir_path): 10 | for file in files: 11 | if file.endswith("_functions.cpp"): 12 | with open(root + '\\' + file) as f: 13 | module = file.split('_')[0] 14 | funcs[module] = [] 15 | in_funcs = False 16 | for line in f.readlines(): 17 | if not in_funcs and not line.startswith('# Functions'): 18 | continue 19 | in_funcs = True 20 | found = regex.match(line) 21 | if found: 22 | clas, fun = found.groups() 23 | funcs[module].append((clas, fun)) 24 | 25 | classFunc_regex = re.compile(r'.*g_classFuncs\["(\w+)"\] = {') 26 | 27 | lua_sdk_path = "C:\\Program Files (x86)\\Steam\\steamapps\\common\\Borderlands 2\\Binaries\\plugins\\include\\sdk\\funcs\\" 28 | from os import listdir 29 | from os.path import isfile, join 30 | onlyfiles = [f for f in listdir(lua_sdk_path) if isfile(join(lua_sdk_path, f)) and f.endswith('.lua')] 31 | print(onlyfiles) 32 | for filename in onlyfiles: 33 | index = 0 34 | l = 0 35 | with open(lua_sdk_path + filename) as f: 36 | with open(lua_sdk_path + filename + '.new', 'w') as f_out: 37 | module_name = filename.split('.')[0] 38 | print(module_name) 39 | classes = set() 40 | for fun in funcs[module_name]: 41 | classes.add(fun[0]) 42 | for clas in classes: 43 | f_out.write('g_classFuncs["{}"] = {{}}\n'.format(clas)) 44 | for line in f.readlines(): 45 | l += 1 46 | found = classFunc_regex.match(line) 47 | if found: 48 | func = found.groups()[0] 49 | clas, fun = funcs[module_name][index] 50 | if func.lower() == fun.lower() or func.lower() == 'event' + fun.lower(): 51 | f_out.write(line.replace(func, '{}"]["{}'.format(clas, func))) 52 | else: 53 | print(module_name) 54 | print('bad') 55 | print(l, clas, fun, func) 56 | exit() 57 | index += 1 58 | else: 59 | f_out.write(line) 60 | 61 | # 62 | # template = \ 63 | # """struct {0}; 64 | # struct TArray_{0} {{ 65 | # struct {0}* Data; 66 | # int Count; 67 | # int Max; 68 | # }}; 69 | # """ 70 | # 71 | # for tarray in (used - defined): 72 | # print(template.format(tarray)) 73 | # 74 | # for tarray in (used - defined): 75 | # print('table.insert(g_TArrayTypes, "{}")'.format(tarray)) 76 | -------------------------------------------------------------------------------- /scripts/merge.py: -------------------------------------------------------------------------------- 1 | import os 2 | import re 3 | 4 | 5 | headers = """class {}(Structure):\n\ 6 | pass\n\n""" 7 | 8 | modules = ['Core', 9 | 'Engine', 10 | 'GameFramework', 11 | 'GFxUI', 12 | 'GearboxFramework', 13 | 'WillowGame', 14 | 'AkAudio', 15 | 'IpDrv', 16 | 'WinDrv', 17 | 'XAudio2', 18 | 'OnlineSubsystemSteamworks', 19 | 'Base'] 20 | 21 | dir_path_python = 'C:/Program Files (x86)/Steam/steamapps/common/Borderlands 2/Binaries/plugins/python/include/sdk/classes/' 22 | used = set() 23 | defined = set() 24 | 25 | 26 | for module in modules: 27 | top = [] 28 | middle = [] 29 | bottom = [] 30 | with open(dir_path_python + module + '.py') as f: 31 | in_bottom = False 32 | for line in f.readlines(): 33 | if 'BL2SDK.g_loadedClasses += [' in line: 34 | in_bottom = True 35 | if in_bottom: 36 | bottom.append(line) 37 | else: 38 | middle.append(line) 39 | if not in_bottom and '._fields_' in line: 40 | top.append(headers.format(line.split('.')[0])) 41 | 42 | with open(dir_path_python + module + '.py', 'w') as f: 43 | for line in top: 44 | f.write(line) 45 | f.write("def __init__():\n") 46 | for line in middle: 47 | f.write(" " + line) 48 | for line in bottom: 49 | f.write(" " + line) -------------------------------------------------------------------------------- /scripts/prune.py: -------------------------------------------------------------------------------- 1 | from os import listdir, remove 2 | 3 | sdk_dir = 'C:\\Users\\abahb\\source\\repos\\BL2-SDK\\bl2-sdk\\pydefs\\' 4 | 5 | relationships = {} 6 | empty = {} 7 | 8 | for filename in listdir(sdk_dir): 9 | is_empty = False 10 | if '_Class' in filename: 11 | with open(sdk_dir + filename) as f: 12 | class_name = None 13 | count = 0 14 | for line in f.readlines(): 15 | if 'py::class_' in line: 16 | count = 0 17 | parent_name = None 18 | obj_def = line.split('<')[1].split('>')[0].strip() 19 | class_name = obj_def.split(',')[0].strip() 20 | if len(obj_def.split(',')) > 1: 21 | parent_name = obj_def.split(',')[1].strip() 22 | if parent_name not in relationships.keys(): 23 | relationships[parent_name] = [] 24 | relationships[parent_name].append(class_name) 25 | if class_name and ';' in line: 26 | if count <= 2: 27 | empty[class_name] = parent_name 28 | is_empty = True 29 | if class_name and line.strip(): 30 | count = count + 1 31 | 32 | actual_empty = {} 33 | for class_name in empty.keys(): 34 | if class_name not in relationships.keys(): 35 | actual_empty[class_name] = empty[class_name] 36 | remove(sdk_dir + "_Classes_{}.cpp".format(class_name)) 37 | 38 | empty = actual_empty 39 | 40 | typemap = "C:/Users/abahb/source/repos/BL2-SDK/bl2-sdk/TypeMap.h" 41 | lines = "" 42 | with open(typemap) as f: 43 | for line in f.readlines(): 44 | if line.startswith(" {"): 45 | class_name = line.split('(')[1].split(')')[0] 46 | if class_name in empty.keys(): 47 | parent = empty[class_name] 48 | if parent != 'UObject': 49 | line = line.replace('({})'.format(class_name), '({})'.format(parent)) 50 | else: 51 | line = "" 52 | lines += line 53 | 54 | with open(typemap, 'w') as f: 55 | f.write(lines) 56 | -------------------------------------------------------------------------------- /scripts/readd_bitfields.py: -------------------------------------------------------------------------------- 1 | import os 2 | import re 3 | 4 | top = """#include "stdafx.h" 5 | // Using ======================================================================= 6 | namespace py = pybind11; 7 | 8 | // Module ====================================================================== 9 | void Export_pystes_{}(py::module &m) 10 | {{ 11 | """ 12 | 13 | bottom = """ 14 | }""" 15 | 16 | files = ['gamedefines.cpp'] 17 | 18 | dir_path_python = 'C:/Users/abahb/source/repos/BL2-SDK/bl2-sdk/pydefs/' 19 | 20 | dir_path_h = 'C:/Users/abahb/source/repos/BL2-SDK/bl2-sdk/' 21 | 22 | classes = {} 23 | for filename in os.listdir(dir_path_h): 24 | if '_classes.h' in filename or '_structs.h' in filename: 25 | objs = {} 26 | with open(dir_path_h + filename) as f: 27 | c = None 28 | fields = [] 29 | for line in f.readlines(): 30 | if line.startswith('class ') or line.startswith('struct '): 31 | c = line.split(' ')[1].strip() 32 | if ' : 1;' in line: 33 | field = line.split(' : 1;')[0].split(' ')[-1] 34 | fields.append(field) 35 | if line == '};\n': 36 | objs[c] = fields 37 | c = None 38 | fields = [] 39 | classes[filename.split('.')[0]] = objs 40 | 41 | bitfield_def = '\t\t.def_property("{field_name}", []({class_name} &self){{return self.{field_name};}}, []({class_name} &self, bool value){{self.{field_name} = value ? 1 : 0;}})\n' 42 | 43 | for s in classes.keys(): 44 | lines = [] 45 | with open(dir_path_python + s + '.cpp') as f: 46 | for line in f.readlines(): 47 | lines.append(line) 48 | if 'class_<' in line: 49 | c = line.split('class_<')[1].strip().split(' ')[0].split(',')[0].strip() 50 | if c in classes[s].keys(): 51 | for field in classes[s][c]: 52 | lines.append(bitfield_def.format(field_name=field, class_name=c)) 53 | with open(dir_path_python + s + '.cpp', 'w') as f: 54 | for line in lines: 55 | f.write(line) 56 | 57 | 58 | # for filename in new_files: 59 | # print('void Export_pystes{}(py::module &m);'.format(filename)) 60 | # with open(dir_path_python + filename + '.cpp', 'w') as f: 61 | # f.write(top.format(filename)) 62 | # for line in new_files[filename]: 63 | # f.write(line) 64 | # f.write(bottom) -------------------------------------------------------------------------------- /scripts/regen_pydefs.py: -------------------------------------------------------------------------------- 1 | import os 2 | import re 3 | 4 | top = """#include "stdafx.h" 5 | // Using ======================================================================= 6 | namespace py = pybind11; 7 | 8 | // Module ====================================================================== 9 | void Export_pystes_{}(py::module &m) 10 | {{ 11 | """ 12 | 13 | bottom = """ 14 | }""" 15 | 16 | class_def = '\tpy::class_< {class_with_parent} >(m, "{class_name}")\n' 17 | bitfield_def = '\t\t.def_property("{field_name}", []({class_name} &self){{return self.{field_name};}}, []({class_name} &self, bool value){{self.{field_name} = value ? 1 : 0;}})\n' 18 | staticclass_def = '\t\t.def_static("StaticClass", &{class_name}::StaticClass, py::return_value_policy::reference)\n' 19 | variable_def = '\t\t.def_readwrite("{var_name}", &{class_name}::{var_name}{policy})\n' 20 | function_def = '\t\t.def{static_def}("{func_name}", &{class_name}::{func_name}{policy})\n' 21 | policy_def = ', py::return_value_policy::reference' 22 | reference_def = '\t\t.def{static_def}("{func_name}", {l}' 23 | reference_template = '[]({class_name} &self {py_args}) {{ {init} {capture} self.{function_name}({new_args}); return py::make_tuple({returned_args}); }})\n' 24 | 25 | dir_path_python = 'C:/Users/abahb/source/repos/BL2-SDK/bl2-sdk/pydefs/' 26 | 27 | dir_path_h = 'C:/Users/abahb/source/repos/BL2-SDK/bl2-sdk/' 28 | 29 | def generate_lambda(clas, fun): 30 | py_args = fun.params 31 | init = "" 32 | replaced = [] 33 | for pointer in fun.pointers: 34 | replaced.append(pointer.name) 35 | if pointer.pre: 36 | pointer_arg = "{} {} py{}".format(pointer.pre, pointer.type, pointer.name) 37 | init += pointer_arg + " = 0 ; " 38 | else: 39 | pointer_arg = "{} py{}".format(pointer.type, pointer.name) 40 | if pointer.type == 'char': 41 | init += pointer_arg + " = malloc(sizeof(char) * 0xFF) ; " 42 | else: 43 | init += pointer_arg + " = ({0})malloc(sizeof({1})) ; ".format(pointer.type, pointer.type[:-1]) 44 | py_args = py_args.replace(pointer_arg.replace(" py", " "), "") 45 | py_args = py_args.strip() 46 | py_args = py_args.replace(', ,', ',') 47 | py_args = py_args.replace(', ,', ',') 48 | py_args = py_args.replace(', ,', ',') 49 | if py_args.startswith(','): 50 | py_args = py_args[1:] 51 | if py_args.endswith(','): 52 | py_args = py_args[:-1] 53 | py_args = py_args.strip() 54 | if py_args: 55 | py_args = ', ' + py_args 56 | new_args = ', '.join([parm.split(' ')[-1] if parm.split(' ')[-1] not in replaced else "py" + parm.split(' ')[-1] for parm in fun.params.split(', ')]) 57 | returned_args = ', '.join(["*py" + parm.name for parm in fun.pointers]) 58 | capture = "" 59 | if fun.return_type != "void": 60 | capture = '{} ret = '.format(fun.return_type) 61 | if returned_args: 62 | returned_args = 'ret, ' + returned_args 63 | else: 64 | returned_args = 'ret' 65 | 66 | return(reference_template.format(class_name = clas, py_args = py_args, init = init, capture = capture, function_name = fun.name, new_args = new_args, returned_args = returned_args)) 67 | 68 | class Function_def(): 69 | def __init__(self, return_type, name, params, pointers, static = False): 70 | self.return_type = return_type 71 | self.name = name 72 | self.params = params 73 | self.pointers = pointers 74 | self.static = static 75 | 76 | class Pointer(): 77 | def __init__(self, pre, t, name): 78 | self.pre = pre 79 | self.type = t 80 | self.name = name 81 | 82 | classes = {} 83 | for filename in os.listdir(dir_path_h): 84 | if '_classes.h' in filename or ('_structs.h' in filename and not '_f_' in filename): 85 | objs = {} 86 | with open(dir_path_h + filename) as f: 87 | c = None 88 | fields = [] 89 | variables = [] 90 | functions = [] 91 | static_functions = [] 92 | reference_functions = {} 93 | for line in f.readlines(): 94 | if line.startswith('class ') or line.startswith('struct '): 95 | c = line.split(' ')[1].strip() 96 | objs[c] = {} 97 | if ':' in line: 98 | objs[c]['parent'] = line.split(' ')[-1].strip() 99 | if '[' in line.split('//')[0]: 100 | continue 101 | if ' : 1;' in line: 102 | field = line.split(' : 1;')[0].split(' ')[-1] 103 | fields.append(field) 104 | elif line.startswith('\t') and not line.startswith('\t\t') and line.strip() != '};': 105 | line = line.split('//')[0].strip() 106 | if line.endswith(';') and line[-2] != ')' and not 'pClassPointer' in line: 107 | needs_reference = '*' in line or 'class ' in line or 'struct ' in line or 'TArray' in line 108 | name = line.split(' ')[-1].split('\t')[-1][:-1].strip() 109 | variables.append((name, needs_reference)) 110 | elif line.endswith(');') and not line.startswith('virtual') and not line.startswith('template') and not (line.startswith('static') and 'StaticClass' in line): 111 | static = line.startswith('static') 112 | if not '*' in line: 113 | needs_reference = line.startswith('TArray') or line.startswith('class') or line.startswith('struct') 114 | name = line.split('(')[0].split(' ')[-1].split('\t')[-1] 115 | functions.append((name, needs_reference, static)) 116 | else: 117 | start, end = line.split('(') 118 | function_name = start.split(' ')[-1] 119 | return_type = ' '.join(start.split(' ')[:-1]).strip() 120 | params = line.split('(')[-1].split(')')[0] 121 | pointers = [] 122 | for param in params.split(', '): 123 | if 'TArray' in param: 124 | continue 125 | if (param.startswith('struct ') or param.startswith('class ')) and '**' in param: 126 | s = param.split(' ') 127 | pointers.append(Pointer(s[0], s[1], s[2])) 128 | elif not param.startswith('struct ') and not param.startswith('class ') and '*' in param: 129 | s = param.split(' ') 130 | pointers.append(Pointer(None, ' '.join(s[:-1]), s[-1])) 131 | if pointers: 132 | reference_functions[function_name] = Function_def(return_type, function_name, params, pointers, static) 133 | else: 134 | needs_reference = line.startswith('TArray') or line.startswith('class') or line.startswith('struct') 135 | name = line.split('(')[0].split(' ')[-1].split('\t')[-1] 136 | functions.append((name, needs_reference, static)) 137 | elif line.startswith('static') and 'StaticClass' in line: 138 | objs[c]['static'] = True 139 | if line == '};\n' or line.endswith(' {};\n'): 140 | objs[c]['bitfields'] = fields 141 | objs[c]['variables'] = variables 142 | objs[c]['functions'] = functions 143 | objs[c]['reference_functions'] = reference_functions 144 | c = None 145 | fields = [] 146 | variables = [] 147 | functions = [] 148 | reference_functions = {} 149 | classes[filename.split('.')[0]] = objs 150 | 151 | 152 | # print('void Export_pystes{}(py::module &m);'.format(filename)) 153 | for module in classes.keys(): 154 | with open(dir_path_python + module + '.cpp', 'w') as f: 155 | f.write(top.format(module)) 156 | objs = classes[module] 157 | for ck in objs.keys(): 158 | c = objs[ck] 159 | name = ck 160 | if 'parent' in c.keys(): 161 | name = '{}, {}'.format(name, c['parent']) 162 | f.write(class_def.format(class_with_parent=name, class_name=ck)) 163 | if ck.startswith('F'): 164 | f.write('\t\t.def(py::init<>())\n') 165 | else: 166 | print('\t{{"{0}", &typeid({1})}},'.format(ck[1:], ck)) 167 | if 'static' in c.keys(): 168 | f.write(staticclass_def.format(class_name=ck)) 169 | if c['bitfields']: 170 | for field in c['bitfields']: 171 | f.write(bitfield_def.format(class_name=ck, field_name=field)) 172 | if c['variables']: 173 | for variable in c['variables']: 174 | policy = policy_def if variable[1] else '' 175 | f.write(variable_def.format(class_name=ck, var_name=variable[0], policy=policy)) 176 | if c['functions']: 177 | for function in c['functions']: 178 | policy = policy_def if function[1] else '' 179 | f.write(function_def.format(static_def='' if not function[2] else '_static', class_name=ck, func_name=function[0], policy=policy)) 180 | if c['reference_functions']: 181 | for reference_function in c['reference_functions'].keys(): 182 | f.write(reference_def.format(static_def='' if not c['reference_functions'][reference_function].static else '_static', func_name=reference_function, l=generate_lambda(ck, c['reference_functions'][reference_function]))) 183 | f.write('\t\t;\n') 184 | f.write(bottom) 185 | 186 | # for s in classes.keys(): 187 | # lines = [] 188 | # with open(dir_path_python + s + '.cpp') as f: 189 | # for line in f.readlines(): 190 | # lines.append(line) 191 | # if 'class_<' in line: 192 | # c = line.split('class_<')[1].strip().split(' ')[0].split(',')[0].strip() 193 | # if c in classes[s].keys(): 194 | # for field in classes[s][c]: 195 | # lines.append(bitfield_def.format(field_name=field, class_name=c)) 196 | # with open(dir_path_python + s + '.cpp', 'w') as f: 197 | # for line in lines: 198 | # f.write(line) 199 | 200 | -------------------------------------------------------------------------------- /scripts/regen_statics.py: -------------------------------------------------------------------------------- 1 | import os 2 | import re 3 | 4 | top = """#include "stdafx.h" 5 | // Using ======================================================================= 6 | namespace py = pybind11; 7 | 8 | // Module ====================================================================== 9 | void Export_pystes_{}(py::module &m) 10 | {{ 11 | """ 12 | 13 | bottom = """ 14 | }""" 15 | 16 | class_def = '\tpy::class_< {class_with_parent} >(m, "{class_name}")\n' 17 | bitfield_def = '\t\t.def_property("{field_name}", []({class_name} &self){{return self.{field_name};}}, []({class_name} &self, bool value){{self.{field_name} = value ? 1 : 0;}})\n' 18 | staticclass_def = '\t\t.def_static("StaticClass", &{class_name}::StaticClass, py::return_value_policy::reference)\n' 19 | variable_def = '\t\t.def_readwrite("{var_name}", &{class_name}::{var_name}{policy})\n' 20 | function_def = '\t\t.def{static_def}("{func_name}", &{class_name}::{func_name}{policy})\n' 21 | policy_def = ', py::return_value_policy::reference' 22 | reference_def = '\t\t.def{static_def}("{func_name}", {l}' 23 | reference_template = '[]({class_name} &self {py_args}) {{ {init} {capture} self.{function_name}({new_args}); return py::make_tuple({returned_args}); }})\n' 24 | 25 | dir_path_python = 'C:/Users/abahb/source/repos/BL2-SDK/bl2-sdk/pydefs/' 26 | 27 | dir_path_h = 'C:/Users/abahb/source/repos/BL2-SDK/bl2-sdk/' 28 | 29 | def generate_lambda(clas, fun): 30 | py_args = fun.params 31 | init = "" 32 | replaced = [] 33 | for pointer in fun.pointers: 34 | replaced.append(pointer.name) 35 | if pointer.pre: 36 | pointer_arg = "{} {} py{}".format(pointer.pre, pointer.type, pointer.name) 37 | init += pointer_arg + " = 0 ; " 38 | else: 39 | pointer_arg = "{} py{}".format(pointer.type, pointer.name) 40 | if pointer.type == 'char': 41 | init += pointer_arg + " = malloc(sizeof(char) * 0xFF) ; " 42 | else: 43 | init += pointer_arg + " = ({0})malloc(sizeof({1})) ; ".format(pointer.type, pointer.type[:-1]) 44 | py_args = py_args.replace(pointer_arg.replace(" py", " "), "") 45 | py_args = py_args.strip() 46 | py_args = py_args.replace(', ,', ',') 47 | py_args = py_args.replace(', ,', ',') 48 | py_args = py_args.replace(', ,', ',') 49 | if py_args.startswith(','): 50 | py_args = py_args[1:] 51 | if py_args.endswith(','): 52 | py_args = py_args[:-1] 53 | py_args = py_args.strip() 54 | if py_args: 55 | py_args = ', ' + py_args 56 | new_args = ', '.join([parm.split(' ')[-1] if parm.split(' ')[-1] not in replaced else "py" + parm.split(' ')[-1] for parm in fun.params.split(', ')]) 57 | returned_args = ', '.join(["*py" + parm.name for parm in fun.pointers]) 58 | capture = "" 59 | if fun.return_type != "void": 60 | capture = '{} ret = '.format(fun.return_type) 61 | if returned_args: 62 | returned_args = 'ret, ' + returned_args 63 | else: 64 | returned_args = 'ret' 65 | 66 | return(reference_template.format(class_name = clas, py_args = py_args, init = init, capture = capture, function_name = fun.name, new_args = new_args, returned_args = returned_args)) 67 | 68 | class Function_def(): 69 | def __init__(self, return_type, name, params, pointers, static = False): 70 | self.return_type = return_type 71 | self.name = name 72 | self.params = params 73 | self.pointers = pointers 74 | self.static = static 75 | 76 | class Pointer(): 77 | def __init__(self, pre, t, name): 78 | self.pre = pre 79 | self.type = t 80 | self.name = name 81 | 82 | classes = {} 83 | for filename in os.listdir(dir_path_h): 84 | if '_classes.h' in filename or ('_structs.h' in filename and not '_f_' in filename): 85 | objs = {} 86 | with open(dir_path_h + filename) as f: 87 | c = None 88 | fields = [] 89 | variables = [] 90 | functions = [] 91 | static_functions = [] 92 | reference_functions = {} 93 | for line in f.readlines(): 94 | if line.startswith('class ') or line.startswith('struct '): 95 | c = line.split(' ')[1].strip() 96 | objs[c] = {} 97 | if ':' in line: 98 | objs[c]['parent'] = line.split(' ')[-1].strip() 99 | if '[' in line.split('//')[0]: 100 | continue 101 | if ' : 1;' in line: 102 | field = line.split(' : 1;')[0].split(' ')[-1] 103 | fields.append(field) 104 | elif line.startswith('\t') and not line.startswith('\t\t') and line.strip() != '};': 105 | line = line.split('//')[0].strip() 106 | if line.endswith(';') and line[-2] != ')' and not 'pClassPointer' in line: 107 | needs_reference = '*' in line or 'class ' in line or 'struct ' in line or 'TArray' in line 108 | name = line.split(' ')[-1].split('\t')[-1][:-1].strip() 109 | variables.append((name, needs_reference)) 110 | elif line.endswith(');') and not line.startswith('virtual') and not line.startswith('template') and not (line.startswith('static') and 'StaticClass' in line): 111 | static = line.startswith('static') 112 | if static: 113 | line = line[len('static '):] 114 | if not '*' in line: 115 | needs_reference = line.startswith('TArray') or line.startswith('class') or line.startswith('struct') 116 | name = line.split('(')[0].split(' ')[-1].split('\t')[-1] 117 | functions.append((name, needs_reference, static)) 118 | else: 119 | start, end = line.split('(') 120 | function_name = start.split(' ')[-1] 121 | return_type = ' '.join(start.split(' ')[:-1]).strip() 122 | params = line.split('(')[-1].split(')')[0] 123 | pointers = [] 124 | for param in params.split(', '): 125 | if 'TArray' in param: 126 | continue 127 | if (param.startswith('struct ') or param.startswith('class ')) and '**' in param: 128 | s = param.split(' ') 129 | pointers.append(Pointer(s[0], s[1], s[2])) 130 | elif not param.startswith('struct ') and not param.startswith('class ') and '*' in param: 131 | s = param.split(' ') 132 | pointers.append(Pointer(None, ' '.join(s[:-1]), s[-1])) 133 | if pointers: 134 | reference_functions[function_name] = Function_def(return_type, function_name, params, pointers, static) 135 | else: 136 | needs_reference = line.startswith('TArray') or line.startswith('class') or line.startswith('struct') 137 | name = line.split('(')[0].split(' ')[-1].split('\t')[-1] 138 | functions.append((name, needs_reference, static)) 139 | elif line.startswith('static') and 'StaticClass' in line: 140 | objs[c]['static'] = True 141 | if line == '};\n' or line.endswith(' {};\n'): 142 | objs[c]['bitfields'] = fields 143 | objs[c]['variables'] = variables 144 | objs[c]['functions'] = functions 145 | objs[c]['reference_functions'] = reference_functions 146 | c = None 147 | fields = [] 148 | variables = [] 149 | functions = [] 150 | reference_functions = {} 151 | classes[filename.split('.')[0]] = objs 152 | 153 | 154 | # print('void Export_pystes{}(py::module &m);'.format(filename)) 155 | for module in classes.keys(): 156 | objs = classes[module] 157 | replacements = {} 158 | for ck in objs.keys(): 159 | c = objs[ck] 160 | name = ck 161 | if 'parent' in c.keys(): 162 | name = '{}, {}'.format(name, c['parent']) 163 | if c['functions']: 164 | for function in c['functions']: 165 | policy = policy_def if function[1] else '' 166 | if function[2] and policy: 167 | replacements['&{}::{})'.format(ck, function[0])] = (function_def.format(static_def='' if not function[2] else '_static', class_name=ck, func_name=function[0], policy=policy)) 168 | if c['reference_functions']: 169 | for reference_function in c['reference_functions'].keys(): 170 | if c['reference_functions'][reference_function].static: 171 | replacements['&{}::{})'.format(ck, c['reference_functions'][reference_function].name)] = (reference_def.format(static_def='' if not c['reference_functions'][reference_function].static else '_static', func_name=reference_function, l=generate_lambda(ck, c['reference_functions'][reference_function]))) 172 | # print('&{}::{})'.format(ck, c['reference_functions'][reference_function].name), replacements['&{}::{})'.format(ck, c['reference_functions'][reference_function].name)]) 173 | with open(dir_path_python + module + '.cpp', 'r') as f: 174 | lines = f.readlines() 175 | with open(dir_path_python + module + '.cpp', 'w') as f: 176 | for line in lines: 177 | for key in replacements.keys(): 178 | if key in line: 179 | line = replacements[key] 180 | f.write(line) -------------------------------------------------------------------------------- /scripts/setup_polymorphism.py: -------------------------------------------------------------------------------- 1 | import os 2 | import re 3 | 4 | files = ['Core_classes.h', 5 | 'Engine_classes.h', 6 | 'GameFramework_classes.h', 7 | 'GFxUI_classes.h', 8 | 'GearboxFramework_classes.h', 9 | 'WillowGame_classes.h', 10 | 'AkAudio_classes.h', 11 | 'IpDrv_classes.h', 12 | 'WinDrv_classes.h', 13 | 'XAudio2_classes.h', 14 | 'OnlineSubsystemSteamworks_classes.h'] 15 | import os 16 | 17 | dir_path_python = 'C:/Users/abahb/source/repos/BL2-SDK/bl2-sdk/' 18 | 19 | template = 'map["{0}"] = &typeid({0});' 20 | 21 | pointers = {} 22 | for clas in files: 23 | with open(dir_path_python + clas) as f: 24 | class_name = None 25 | for line in f.readlines(): 26 | if line.startswith('class '): 27 | class_name = line.split(" ")[1].strip() 28 | if line.startswith(' pClassPointer = '): 29 | num = line.split('[')[-1].split(']')[0] 30 | pointers[int(num)] = class_name 31 | 32 | for num in sorted(pointers.keys()): 33 | print(template.format(pointers[num])) -------------------------------------------------------------------------------- /scripts/sort_relationships.py: -------------------------------------------------------------------------------- 1 | from os import listdir 2 | 3 | sdk_dir = 'C:\\Users\\abahb\\source\\repos\\BL2-SDK\\bl2-sdk\\pydefs\\' 4 | 5 | relationships = {} 6 | 7 | for filename in listdir(sdk_dir): 8 | with open(sdk_dir + filename) as f: 9 | for line in f.readlines(): 10 | if 'py::class_' in line: 11 | obj_def = line.split('<')[1].split('>')[0].strip() 12 | objs = obj_def.split(',') 13 | if len(objs) > 1: 14 | child = objs[0].strip() 15 | parent = objs[1].strip() 16 | if parent not in relationships.keys(): 17 | relationships[parent] = [] 18 | relationships[parent].append(child) 19 | if objs[0] not in relationships.keys(): 20 | relationships[objs[0]] = [] 21 | 22 | to_check = ['UObject'] 23 | while to_check: 24 | current = to_check.pop() 25 | to_check += relationships[current] 26 | del relationships[current] 27 | print(" Export_pystes_{}(m);".format(current)) 28 | for key in relationships.keys(): 29 | print(" Export_pystes_{}(m);".format(key)) 30 | -------------------------------------------------------------------------------- /scripts/split_sdk.py: -------------------------------------------------------------------------------- 1 | import os 2 | import re 3 | 4 | top = """#include "stdafx.h" 5 | // Using ======================================================================= 6 | namespace py = pybind11; 7 | 8 | // Module ====================================================================== 9 | void Export_pystes_{}(py::module &m) 10 | {{ 11 | """ 12 | 13 | bottom = """ 14 | }""" 15 | 16 | files = ['gamedefines.cpp'] 17 | 18 | dir_path_python = 'C:/Users/abahb/source/repos/BL2-SDK/bl2-sdk/pydefs/' 19 | 20 | dir_path_h = 'C:/Users/abahb/source/repos/BL2-SDK/bl2-sdk/' 21 | 22 | structs = {} 23 | classes = {} 24 | for filename in os.listdir(dir_path_h): 25 | if '_classes.h' in filename or '_structs.h' in filename: 26 | objs = [] 27 | with open(dir_path_h + filename) as f: 28 | for line in f.readlines(): 29 | if line.startswith('class ') or line.startswith('struct '): 30 | name = line.split(' ')[1].strip() 31 | objs.append(name) 32 | if '_classes.h' in filename: 33 | classes[filename.split('.')[0]] = objs 34 | else: 35 | structs[filename.split('.')[0]] = objs 36 | 37 | 38 | 39 | 40 | for s in structs.keys(): 41 | with open(dir_path_python + s + '.cpp', 'w') as f: 42 | f.write(top.format(s)) 43 | for sf in structs[s]: 44 | try: 45 | with open(dir_path_python + '_Structs_{}.cpp'.format(sf)) as fs: 46 | lines = fs.readlines() 47 | write = False 48 | for line in lines: 49 | if 'class_' in line: 50 | write = True 51 | if write: 52 | f.write(line) 53 | if ';' in line: 54 | write = False 55 | except Exception as e: 56 | print(e) 57 | f.write(bottom) 58 | 59 | for s in classes.keys(): 60 | with open(dir_path_python + s + '.cpp', 'w') as f: 61 | f.write(top.format(s)) 62 | for sf in classes[s]: 63 | try: 64 | with open(dir_path_python + '_Classes_{}.cpp'.format(sf)) as fs: 65 | lines = fs.readlines() 66 | write = False 67 | for line in lines: 68 | if 'class_' in line: 69 | write = True 70 | if write: 71 | f.write(line) 72 | if line.strip() == ';': 73 | write = False 74 | except Exception as e: 75 | print(e) 76 | f.write(bottom) 77 | 78 | 79 | # for filename in new_files: 80 | # print('void Export_pystes{}(py::module &m);'.format(filename)) 81 | # with open(dir_path_python + filename + '.cpp', 'w') as f: 82 | # f.write(top.format(filename)) 83 | # for line in new_files[filename]: 84 | # f.write(line) 85 | # f.write(bottom) -------------------------------------------------------------------------------- /scripts/tarray_finder.py: -------------------------------------------------------------------------------- 1 | import os 2 | import re 3 | 4 | dir_path = os.path.dirname(os.path.realpath(__file__)) 5 | used = set() 6 | defined = set() 7 | 8 | used_regex = re.compile(r'.* TArray_(\w+).*') 9 | defined_regex = re.compile(r'.*struct TArray_(\w+) {') 10 | 11 | for root, dirs, files in os.walk(dir_path): 12 | for file in files: 13 | if file.endswith(".lua"): 14 | with open(root + '\\' + file) as f: 15 | for line in f.readlines(): 16 | found = used_regex.match(line) 17 | if found: 18 | used.add(found.groups()[0]) 19 | 20 | found = defined_regex.match(line) 21 | if found: 22 | defined.add(found.groups()[0]) 23 | 24 | 25 | template = \ 26 | """struct {0}; 27 | struct TArray_{0} {{ 28 | struct {0}* Data; 29 | int Count; 30 | int Max; 31 | }}; 32 | """ 33 | 34 | for tarray in (used - defined): 35 | print(template.format(tarray)) 36 | 37 | for tarray in (used - defined): 38 | print('table.insert(g_TArrayTypes, "{}")'.format(tarray)) 39 | -------------------------------------------------------------------------------- /src/AntiDebug.cpp: -------------------------------------------------------------------------------- 1 | #include "stdafx.h" 2 | #include "CSimpleDetour.h" 3 | #include "Logging.h" 4 | #include "Exceptions.h" 5 | #include "Util.h" 6 | 7 | #include 8 | #define STATUS_SUCCESS ((NTSTATUS)0x00000000L) 9 | #define STATUS_PORT_NOT_SET ((NTSTATUS)0xC0000353L) 10 | 11 | namespace UnrealSDK 12 | { 13 | typedef NTSTATUS (WINAPI* tNtSIT)(HANDLE, THREAD_INFORMATION_CLASS, PVOID, ULONG); 14 | tNtSIT pNtSetInformationThread = nullptr; 15 | 16 | NTSTATUS NTAPI hkNtSetInformationThread( 17 | __in HANDLE ThreadHandle, 18 | __in THREAD_INFORMATION_CLASS ThreadInformationClass, 19 | __in PVOID ThreadInformation, 20 | __in ULONG ThreadInformationLength) 21 | { 22 | if (ThreadInformationClass == 17) // ThreadHideFromDebugger 23 | { 24 | Logging::Log("[AntiDebug] NtSetInformationThread called with ThreadHideFromDebugger\n"); 25 | return STATUS_SUCCESS; 26 | } 27 | 28 | return pNtSetInformationThread(ThreadHandle, ThreadInformationClass, ThreadInformation, 29 | ThreadInformationLength); 30 | } 31 | 32 | typedef NTSTATUS (WINAPI* tNtQIP)(HANDLE, PROCESSINFOCLASS, PVOID, ULONG, PULONG); 33 | tNtQIP pNtQueryInformationProcess = nullptr; 34 | 35 | NTSTATUS WINAPI hkNtQueryInformationProcess( 36 | __in HANDLE ProcessHandle, 37 | __in PROCESSINFOCLASS ProcessInformationClass, 38 | __out PVOID ProcessInformation, 39 | __in ULONG ProcessInformationLength, 40 | __out_opt PULONG ReturnLength) 41 | { 42 | if (ProcessInformationClass == 30) // ProcessDebugObjectHandle 43 | { 44 | return STATUS_PORT_NOT_SET; 45 | } 46 | 47 | return pNtQueryInformationProcess(ProcessHandle, ProcessInformationClass, ProcessInformation, 48 | ProcessInformationLength, ReturnLength); 49 | } 50 | 51 | void HookAntiDebug() 52 | { 53 | HMODULE hNtdll = GetModuleHandle(L"ntdll.dll"); 54 | if (!hNtdll) 55 | { 56 | throw FatalSDKException(7000, Util::Format("GetModuleHandle failed for ntdll.dll (Error = 0x%X)", 57 | GetLastError())); 58 | } 59 | 60 | pNtSetInformationThread = (tNtSIT)GetProcAddress(hNtdll, "NtSetInformationThread"); 61 | if (!pNtSetInformationThread) 62 | { 63 | throw FatalSDKException( 64 | 7001, Util::Format("GetProcAddress failed for NtSetInformationThread (Error = 0x%X)", GetLastError())); 65 | } 66 | 67 | SETUP_SIMPLE_DETOUR(detNtSIT, pNtSetInformationThread, hkNtSetInformationThread); 68 | detNtSIT.Attach(); 69 | Logging::Log("[AntiDebug] Hook added for NtSetInformationThread\n"); 70 | 71 | pNtQueryInformationProcess = (tNtQIP)GetProcAddress(hNtdll, "NtQueryInformationProcess"); 72 | if (!pNtQueryInformationProcess) 73 | { 74 | throw FatalSDKException( 75 | 7002, Util::Format("GetProcAddress failed for NtQueryInformationProcess (Error = 0x%X)", 76 | GetLastError())); 77 | } 78 | 79 | SETUP_SIMPLE_DETOUR(detNtQIP, pNtQueryInformationProcess, hkNtQueryInformationProcess); 80 | detNtQIP.Attach(); 81 | Logging::Log("[AntiDebug] Hook added for NtQueryInformationProcess\n"); 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /src/CHookManager.cpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "stdafx.h" 3 | #include "CHookManager.h" 4 | 5 | void CHookManager::Register(const std::string& FuncName, const std::string& HookName, 6 | const std::function& FuncHook) 7 | { 8 | char funcNameChar[255]; 9 | strcpy(funcNameChar, FuncName.c_str()); 10 | 11 | // Create pair to insert 12 | tHookPair hookPair = std::make_pair(HookName, FuncHook); 13 | 14 | auto iHooks = hooks.find(FuncName); 15 | if (iHooks != hooks.end()) 16 | iHooks->second.insert(hookPair); 17 | else 18 | { 19 | tHookMap newMap; 20 | newMap.insert(hookPair); 21 | hooks.emplace(FuncName, newMap); 22 | } 23 | 24 | Logging::LogD("[HookManager] (%s) Hook \"%s\" added as hook for \"%s\"\n", this->debugName.c_str(), 25 | hookPair.first.c_str(), FuncName.c_str()); 26 | } 27 | 28 | bool CHookManager::Remove(const std::string& FuncName, const std::string& HookName) 29 | { 30 | auto iHooks = hooks.find(FuncName); 31 | if (iHooks == hooks.end() || iHooks->second.find(HookName) == iHooks->second.end()) 32 | { 33 | Logging::LogD("[HookManager] (%s) ERROR: Failed to remove hook \"%s\" for \"%s\"\n", this->debugName.c_str(), 34 | HookName.c_str(), FuncName.c_str()); 35 | return false; 36 | } 37 | 38 | iHooks->second.erase(HookName); 39 | return true; 40 | } 41 | 42 | bool CHookManager::ProcessHooks(const std::string& FuncName, const UObject* Caller, const UFunction* Func, 43 | const FStruct* Params) 44 | { 45 | auto iHooks = hooks.find(FuncName); 46 | 47 | if (iHooks != hooks.end()) 48 | { 49 | tHookMap matchedHooks = iHooks->second; 50 | 51 | for (auto& hook : matchedHooks) 52 | if (!hook.second(const_cast(Caller), const_cast(Func), const_cast(Params))) 53 | return false; 54 | } 55 | 56 | iHooks = hooks.find(const_cast(Caller)->GetObjectName() + "." + const_cast(Func)->GetName()); 57 | 58 | if (iHooks != hooks.end()) 59 | { 60 | tHookMap matchedHooks = iHooks->second; 61 | 62 | for (auto& hook : matchedHooks) 63 | if (!hook.second(const_cast(Caller), const_cast(Func), const_cast(Params))) 64 | return false; 65 | } 66 | return true; 67 | } 68 | 69 | 70 | bool CHookManager::HasHook(UObject* Caller, UFunction* Func) 71 | { 72 | auto iHooks = hooks.find(Func->GetObjectName()); 73 | if (iHooks != hooks.end() && !iHooks->second.empty()) 74 | return true; 75 | iHooks = hooks.find(Caller->GetObjectName() + "." + Func->GetName()); 76 | return (iHooks != hooks.end() && !iHooks->second.empty()); 77 | } 78 | 79 | bool CHookManager::ProcessHooks(UObject* Caller, FFrame& Stack, void* const Result, UFunction* Function) 80 | { 81 | const auto iHooks = hooks.find(Function->GetObjectName()); 82 | 83 | // Even though we check in the next function, check here to avoid messing with the stack when we don't need to 84 | if (iHooks != hooks.end() || hooks.find(Caller->GetObjectName() + "." + Function->GetName()) != hooks.end()) 85 | { 86 | UProperty* ReturnParm; 87 | char* Frame = static_cast(calloc(1, Function->ParamsSize)); 88 | for (auto* Property = static_cast(Function->Children); Stack.Code[0] != 0x16; Property = static_cast(Property-> 89 | Next)) 90 | { 91 | const bool bIsReturnParam = ((Property->PropertyFlags & 0x400) != 0); 92 | if (bIsReturnParam) 93 | { 94 | ReturnParm = Property; 95 | continue; 96 | } 97 | UnrealSDK::pFrameStep(&Stack, Stack.Object, Frame + Property->Offset_Internal); 98 | } 99 | auto FrameStruct = FStruct{ Function, (void*) Frame }; 100 | const bool ret = ProcessHooks(Function->GetObjectName(), Caller, Function, &FrameStruct); 101 | //if (!ret) { 102 | // if (ReturnParm) 103 | // { 104 | // memcpy(Result, Frame + ReturnParm->Offset_Internal, ReturnParm->ElementSize); 105 | // Stack.Outparams = (FOutParmRec *)malloc(sizeof(FOutParmRec)); 106 | // Stack.Outparams->Property = ReturnParm; 107 | // Stack.Outparams->PropAddr = (unsigned char *)Result; 108 | // } 109 | // FOutParmRec** LastOut = &Stack.Outparams; 110 | // for (UProperty* Property = (UProperty *)Function->Children; Property; Property = (UProperty*)Property->Next) { 111 | // const bool bIsReturnParam = ((Property->PropertyFlags & 0x400) != 0); 112 | // if (bIsReturnParam) 113 | // continue; 114 | // if (Property->PropertyFlags & 0x100) { 115 | // FOutParmRec *NewOutParm = (FOutParmRec *)malloc(sizeof(FOutParmRec)); 116 | // NewOutParm->Property = Property; 117 | // NewOutParm->PropAddr = (unsigned char *)malloc(sizeof(Property->ElementSize)); 118 | // memcpy(NewOutParm->PropAddr, Frame + Property->Offset_Internal, Property->ElementSize); 119 | // if (*LastOut) { 120 | // (*LastOut)->NextOutParm = NewOutParm; 121 | // LastOut = &(*LastOut)->NextOutParm; 122 | // } 123 | // else { 124 | // *LastOut = NewOutParm; 125 | // } 126 | // } 127 | // } 128 | //} 129 | //LogOutParams(Stack); 130 | memset(Frame, 0, Function->ParamsSize); 131 | free(Frame); 132 | return ret; 133 | } 134 | return true; 135 | } 136 | -------------------------------------------------------------------------------- /src/CPythonInterface.cpp: -------------------------------------------------------------------------------- 1 | #include "stdafx.h" 2 | #include "CPythonInterface.h" 3 | #include "Logging.h" 4 | #include "Settings.h" 5 | #include "Util.h" 6 | #include "UnrealSDK.h" 7 | #include 8 | #include 9 | #include 10 | 11 | bool VerifyPythonFunction(py::object funcHook) { 12 | PyObject* obj = funcHook.ptr(); 13 | if (!obj) { 14 | Logging::LogF("[Error] Object passed to hook is null\n"); 15 | return false; 16 | } 17 | if (!PyFunction_Check(obj)) { 18 | Logging::LogF("[Error] Object passed to hook is not a function\n"); 19 | return false; 20 | } 21 | PyCodeObject* code = (PyCodeObject*)PyFunction_GetCode(obj); 22 | if (!code) { 23 | Logging::LogF("[Error] Unable to retrive code from object passed to hook\n"); 24 | return false; 25 | } 26 | if (code->co_argcount != 3) { 27 | Logging::LogF("[Error] Function passed to hook must have exactly 3 arguments\n"); 28 | return false; 29 | } 30 | return true; 31 | } 32 | 33 | void RegisterHook(const std::string& funcName, const std::string& hookName, py::object funcHook) { 34 | if (!VerifyPythonFunction(funcHook)) { 35 | return; 36 | } 37 | UnrealSDK::RegisterHook(funcName, hookName, [funcHook](UObject* caller, UFunction* function, FStruct* params) { 38 | try { 39 | py::object py_caller = cast(caller, py::return_value_policy::reference); 40 | py::object py_function = cast(function, py::return_value_policy::reference); 41 | py::object py_params = cast(params, py::return_value_policy::reference); 42 | py::object ret = funcHook(py_caller, py_function, py_params); 43 | return ret.cast(); 44 | } catch (std::exception e) { 45 | Logging::LogF(e.what()); 46 | } 47 | return true; 48 | }); 49 | } 50 | 51 | namespace py = pybind11; 52 | 53 | PYBIND11_EMBEDDED_MODULE(unrealsdk, m) 54 | { 55 | Export_pystes_gamedefines(m); 56 | Export_pystes_Core_structs(m); 57 | Export_pystes_Core_classes(m); 58 | Export_pystes_TArray(m); 59 | 60 | m.def("GetVersion", []() { return py::make_tuple(VERSION_MAJOR, VERSION_MINOR, VERSION_PATCH); }); 61 | m.def("Log", [](py::args args) { 62 | std::ostringstream msg; 63 | for (py::size_t i = 0; i < args.size(); i++) { 64 | if (i > 0) { 65 | msg << " "; 66 | } 67 | msg << py::str(args[i]); 68 | } 69 | Logging::LogPy(msg.str()); 70 | }); 71 | m.def("LoadPackage", &UnrealSDK::LoadPackage, py::arg("filename"), py::arg("flags") = 0, py::arg("force") = false); 72 | m.def("KeepAlive", &UnrealSDK::KeepAlive); 73 | m.def("GetPackageObject", &UObject::GetPackageObject, py::return_value_policy::reference); 74 | m.def("FindAll", &UObject::FindAll, py::arg("InStr"), py::arg("IncludeSubclasses") = false, 75 | py::return_value_policy::reference); 76 | m.def("FindClass", &UObject::FindClass, py::arg("ClassName"), py::arg("Lookup") = false, 77 | py::return_value_policy::reference); 78 | m.def("FindObject", [](char* ClassName, char* ObjectFullName) { return UObject::Find(ClassName, ObjectFullName); }, 79 | py::return_value_policy::reference); 80 | m.def("FindObject", [](UClass* Class, char* ObjectFullName) { return UObject::Find(Class, ObjectFullName); }, 81 | py::return_value_policy::reference); 82 | m.def("LoadObject", [](char* ClassName, char* ObjectFullName) { return UObject::Load(ClassName, ObjectFullName); }, 83 | py::return_value_policy::reference); 84 | m.def("LoadObject", [](UClass* Class, char* ObjectFullName) { return UObject::Load(Class, ObjectFullName); }, 85 | py::return_value_policy::reference); 86 | //m.def("LoadTexture", &UnrealSDK::LoadTexture, py::return_value_policy::reference); 87 | m.def("SetLoggingLevel", &Logging::SetLoggingLevel); 88 | m.def("ConstructObject", &UnrealSDK::ConstructObject, "Construct Objects", py::arg("Class"), 89 | py::arg("Outer") = UnrealSDK::GetEngine()->Outer, py::arg("Name") = FName(), py::arg("SetFlags") = 0x1, 90 | py::arg("InternalSetFlags") = 0x00, py::arg("Template") = (UObject*)nullptr, 91 | py::arg("Error") = (FOutputDevice *)nullptr, py::arg("InstanceGraph") = (void*)nullptr, 92 | py::arg("bAssumeTemplateIsArchetype") = (int)0, py::return_value_policy::reference); 93 | m.def("ConstructObject", [](char* ClassName, UObject* Outer, FName Name, unsigned int SetFlags, 94 | unsigned int InternalSetFlags, UObject* Template, FOutputDevice* Error, 95 | void* InstanceGraph, int bAssumeTemplateIsArchetype) 96 | { 97 | return UnrealSDK::ConstructObject(UObject::FindClass(ClassName), Outer, Name, SetFlags, InternalSetFlags, 98 | Template, 99 | Error, InstanceGraph, bAssumeTemplateIsArchetype); 100 | }, "Construct Objects", py::arg("Class"), py::arg("Outer") = UnrealSDK::GetEngine()->Outer, 101 | py::arg("Name") = FName(), 102 | py::arg("SetFlags") = 0x1, py::arg("InternalSetFlags") = 0x00, py::arg("Template") = (UObject*)nullptr, 103 | py::arg("Error") = (FOutputDevice *)nullptr, py::arg("InstanceGraph") = (void*)nullptr, 104 | py::arg("bAssumeTemplateIsArchetype") = (int)0, py::return_value_policy::reference); 105 | m.def("RegisterHook", &RegisterHook); 106 | m.def("GetEngine", &UnrealSDK::GetEngine, py::return_value_policy::reference); 107 | m.def("RemoveHook", [](const std::string& funcName, const std::string& hookName) 108 | { 109 | UnrealSDK::RemoveHook(funcName, hookName); 110 | }); 111 | m.def("RunHook", [](const std::string& funcName, const std::string& hookName, py::object funcHook) 112 | { 113 | UnrealSDK::RemoveHook(funcName, hookName); 114 | RegisterHook(funcName, hookName, funcHook); 115 | }); 116 | m.def("DoInjectedCallNext", &UnrealSDK::DoInjectedCallNext); 117 | m.def("LogAllCalls", &UnrealSDK::LogAllCalls); 118 | m.def("CallPostEdit", [](bool NewValue) { UnrealSDK::gCallPostEdit = NewValue; }); 119 | } 120 | 121 | void AddToConsoleLog(UConsole* console, FString input) 122 | { 123 | int prev = (console->HistoryTop - 1) % 16; 124 | if (!console->History[prev].Data || strcmp(input.AsString(), console->History[prev].AsString())) 125 | { 126 | console->PurgeCommandFromHistory(input); 127 | console->History[console->HistoryTop] = input; 128 | console->HistoryTop = (console->HistoryTop + 1) % 16; 129 | if ((console->HistoryBot == -1) || console->HistoryBot == console->HistoryTop) 130 | console->HistoryBot = (console->HistoryBot + 1) % 16; 131 | } 132 | console->HistoryCur = console->HistoryTop; 133 | console->SaveConfig(); 134 | } 135 | 136 | bool CheckPythonCommand(UObject* caller, UFunction* function, FStruct* params) 137 | { 138 | FString* command = ((FHelper*)params->base)->GetStrProperty( 139 | (UProperty*)params->structType->FindChildByName(FName("command")), 140 | 0 141 | ); 142 | char* input = command->AsString(); 143 | if (strncmp("py ", input, 3) == 0) 144 | { 145 | AddToConsoleLog((UConsole *)caller, *command); 146 | Logging::LogF("\n>>> %s <<<\n", input); 147 | UnrealSDK::Python->DoString(input + 3); 148 | } 149 | else if (strncmp("pyexec ", input, 7) == 0) 150 | { 151 | AddToConsoleLog((UConsole *)caller, *command); 152 | Logging::LogF("\n>>> %s <<<\n", input); 153 | UnrealSDK::Python->DoFile(input + 7); 154 | } 155 | else 156 | ((UConsole *)caller)->ConsoleCommand(*command); 157 | return false; 158 | } 159 | 160 | CPythonInterface::CPythonInterface() 161 | { 162 | m_modulesInitialized = false; 163 | InitializeState(); 164 | 165 | UnrealSDK::RegisterHook("Engine.Console.ShippingConsoleCommand", "CheckPythonCommand", &CheckPythonCommand); 166 | } 167 | 168 | CPythonInterface::~CPythonInterface() 169 | { 170 | if (m_modulesInitialized) 171 | { 172 | CallShutdownFuncs(); 173 | } 174 | 175 | CleanupState(); 176 | } 177 | 178 | void CPythonInterface::InitializeState() 179 | { 180 | try 181 | { 182 | py::initialize_interpreter(); 183 | m_mainNamespace = py::module::import("__main__"); 184 | } 185 | catch (std::exception e) 186 | { 187 | Logging::LogF("%s", e.what()); 188 | } 189 | } 190 | 191 | void CPythonInterface::CleanupState() 192 | { 193 | py::finalize_interpreter(); 194 | } 195 | 196 | std::vector getSubdirs(const std::wstring& path) 197 | { 198 | WIN32_FIND_DATA findfiledata; 199 | HANDLE hFind = INVALID_HANDLE_VALUE; 200 | 201 | wchar_t fullpath[MAX_PATH]; 202 | GetFullPathName(path.c_str(), MAX_PATH, fullpath, nullptr); 203 | std::wstring fp(fullpath); 204 | 205 | std::vector output{}; 206 | hFind = FindFirstFile((fp + L"\\*").c_str(), &findfiledata); 207 | if (hFind != INVALID_HANDLE_VALUE) 208 | { 209 | do 210 | { 211 | if ((findfiledata.dwFileAttributes | FILE_ATTRIBUTE_DIRECTORY) == FILE_ATTRIBUTE_DIRECTORY 212 | && (findfiledata.cFileName[0] != '.') && wcscmp(L"__pycache__", findfiledata.cFileName)) 213 | { 214 | output.push_back(findfiledata.cFileName); 215 | } 216 | } 217 | while (FindNextFile(hFind, &findfiledata) != 0); 218 | } 219 | return output; 220 | } 221 | 222 | PythonStatus CPythonInterface::InitializeModules() 223 | { 224 | m_modulesInitialized = false; 225 | SetPaths(); 226 | Logging::LogPy(Util::Format("[Python] Version: %d.%d.%d", VERSION_MAJOR, VERSION_MINOR, VERSION_PATCH)); 227 | 228 | try 229 | { 230 | py::module::import("unrealsdk"); 231 | py::module::import("Mods"); 232 | 233 | // Also make these accessable on console 234 | DoString("import unrealsdk, Mods"); 235 | } 236 | catch (std::exception e) 237 | { 238 | Logging::LogF(e.what()); 239 | Logging::Log("[Python] Failed to initialize Python modules\n"); 240 | return PYTHON_MODULE_ERROR; 241 | } 242 | 243 | Logging::Log("[Python] Python initialized (" PYTHON_ABI_STRING ")\n"); 244 | m_modulesInitialized = true; 245 | return PYTHON_OK; 246 | } 247 | 248 | void CPythonInterface::SetPaths() 249 | { 250 | m_PythonPath = Util::Narrow(Settings::GetPythonFile(L"")); 251 | } 252 | 253 | int CPythonInterface::DoFile(const char* filename) 254 | { 255 | return DoFileAbsolute(Util::Format("%s\\%s", m_PythonPath.c_str(), filename).c_str()); 256 | } 257 | 258 | int CPythonInterface::DoString(const char* command) 259 | { 260 | try 261 | { 262 | py::exec(command); 263 | } 264 | catch (std::exception e) 265 | { 266 | Logging::LogF("%s", e.what()); 267 | } 268 | return 0; 269 | } 270 | 271 | int CPythonInterface::DoFileAbsolute(const char* path) 272 | { 273 | try 274 | { 275 | py::eval_file(path); 276 | } 277 | catch (std::exception e) 278 | { 279 | Logging::LogF("%s", e.what()); 280 | } 281 | return 0; 282 | } 283 | 284 | void CPythonInterface::CallShutdownFuncs() 285 | { 286 | } 287 | 288 | py::object CPythonInterface::GetPythonNamespace() 289 | { 290 | return m_mainNamespace; 291 | } 292 | -------------------------------------------------------------------------------- /src/CSigScan.cpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "stdafx.h" 3 | #include "MemorySignature.h" 4 | #include "CSigScan.h" 5 | #include "Util.h" 6 | #include "Exceptions.h" 7 | // Based off CSigScan from AlliedModders 8 | 9 | CSigScan::CSigScan(const wchar_t* moduleName) 10 | { 11 | m_moduleHandle = GetModuleHandle(moduleName); 12 | if (m_moduleHandle == nullptr) 13 | { 14 | throw FatalSDKException(3000, Util::Format("Sigscan failed (GetModuleHandle returned NULL, Error = %d)", 15 | GetLastError())); 16 | } 17 | 18 | void* pAddr = m_moduleHandle; 19 | 20 | MEMORY_BASIC_INFORMATION mem; 21 | 22 | if (!VirtualQuery(pAddr, &mem, sizeof(mem))) 23 | { 24 | throw FatalSDKException(3001, Util::Format("Sigscan failed (VirtualQuery returned NULL, Error = %d)", 25 | GetLastError())); 26 | } 27 | 28 | m_pModuleBase = (char*)mem.AllocationBase; 29 | if (m_pModuleBase == nullptr) 30 | { 31 | throw FatalSDKException(3002, "Sigscan failed (mem.AllocationBase was NULL)"); 32 | } 33 | 34 | IMAGE_DOS_HEADER* dos = (IMAGE_DOS_HEADER*)mem.AllocationBase; 35 | IMAGE_NT_HEADERS* pe = (IMAGE_NT_HEADERS*)((unsigned long)dos + (unsigned long)dos->e_lfanew); 36 | 37 | if (pe->Signature != IMAGE_NT_SIGNATURE) 38 | { 39 | throw FatalSDKException(3003, "Sigscan failed (pe points to a bad location)"); 40 | } 41 | 42 | m_moduleLen = (size_t)pe->OptionalHeader.SizeOfImage; 43 | } 44 | 45 | void* CSigScan::Scan(const MemorySignature& sigStruct) 46 | { 47 | return Scan(sigStruct.Sig, sigStruct.Mask, sigStruct.Length); 48 | } 49 | 50 | void* CSigScan::Scan(const char* sig, const char* mask) 51 | { 52 | int sigLength = strlen(mask); 53 | return Scan(sig, mask, sigLength); 54 | } 55 | 56 | void* CSigScan::Scan(const char* sig, const char* mask, int sigLength) 57 | { 58 | char* pData = m_pModuleBase; 59 | char* pEnd = m_pModuleBase + m_moduleLen; 60 | 61 | while (pData < pEnd) 62 | { 63 | int i; 64 | for (i = 0; i < sigLength; i++) 65 | { 66 | if (mask[i] != '?' && sig[i] != pData[i]) 67 | break; 68 | } 69 | 70 | // The for loop finished on its own accord 71 | if (i == sigLength) 72 | { 73 | return (void*)pData; 74 | } 75 | 76 | pData++; 77 | } 78 | Logging::LogF("Sigscan failed (Signature not found, Mask = %s)", Util::StringToHex(sig, sigLength).c_str()); 79 | return nullptr; 80 | } 81 | -------------------------------------------------------------------------------- /src/CSimpleDetour.cpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "stdafx.h" 3 | #include "CSimpleDetour.h" 4 | #include "Util.h" 5 | #include "Exceptions.h" 6 | 7 | CSimpleDetour::CSimpleDetour(void** old, void* replacement) 8 | { 9 | m_fnOld = old; 10 | m_fnReplacement = replacement; 11 | } 12 | 13 | void CSimpleDetour::Attach() 14 | { 15 | DetourTransactionBegin(); 16 | //DetourUpdateThread(GetCurrentThread()); 17 | 18 | DetourAttach(m_fnOld, m_fnReplacement); 19 | 20 | const LONG result = DetourTransactionCommit(); 21 | if (result != NO_ERROR) 22 | { 23 | throw FatalSDKException(4000, Util::Format("Failed to attach detour (Old = 0x%p, Hook = 0x%p, Result = 0x%X)", 24 | m_fnOld, m_fnReplacement, result)); 25 | } 26 | 27 | m_bAttached = true; 28 | } 29 | 30 | void CSimpleDetour::Detach() 31 | { 32 | if (!m_bAttached) 33 | return; 34 | 35 | DetourTransactionBegin(); 36 | DetourUpdateThread(GetCurrentThread()); 37 | 38 | DetourDetach(m_fnOld, m_fnReplacement); 39 | 40 | LONG result = DetourTransactionCommit(); 41 | if (result != NO_ERROR) 42 | { 43 | throw FatalSDKException(4001, Util::Format("Failed to detach detour (Old = 0x%p, Hook = 0x%p, Result = 0x%X)", 44 | m_fnOld, m_fnReplacement, result)); 45 | } 46 | 47 | m_bAttached = false; 48 | } 49 | -------------------------------------------------------------------------------- /src/Logging.cpp: -------------------------------------------------------------------------------- 1 | #define WIN32_LEAN_AND_MEAN 2 | #include 3 | #include 4 | #include "stdafx.h" 5 | #include "UnrealSdk.h" 6 | #include "Logging.h" 7 | #include "Util.h" 8 | #include "Exceptions.h" 9 | 10 | namespace Logging 11 | { 12 | HANDLE gLogFile = nullptr; 13 | bool gLogToExternalConsole = true; 14 | bool gLogToFile = true; 15 | bool gLogToGameConsole = false; 16 | 17 | void LogToFile(const char* Buff, const int Len) 18 | { 19 | if (gLogFile != INVALID_HANDLE_VALUE) 20 | { 21 | DWORD bytesWritten = 0; 22 | WriteFile(gLogFile, Buff, Len, &bytesWritten, nullptr); 23 | } 24 | } 25 | 26 | void LogWinConsole(const char* Buff, const int Len) 27 | { 28 | const HANDLE output = GetStdHandle(STD_OUTPUT_HANDLE); 29 | DWORD bytesWritten = 0; 30 | WriteFile(output, Buff, Len, &bytesWritten, nullptr); 31 | } 32 | 33 | void Log(const char* Formatted, int Length) 34 | { 35 | std::string out = Formatted; 36 | if (Formatted[strlen(Formatted) - 1] != '\n') 37 | { 38 | out += "\n"; 39 | } 40 | const char* outc = out.c_str(); 41 | 42 | if (Length == 0) 43 | Length = strlen(outc); 44 | 45 | if (gLogToExternalConsole) 46 | LogWinConsole(outc, Length); 47 | 48 | if (gLogToFile) 49 | LogToFile(outc, Length); 50 | 51 | if (UnrealSDK::gameConsole != nullptr) 52 | { 53 | // It seems that Unreal will automatically put a newline on the end of a 54 | // console output, but if there's already a \n at the end, then it won't 55 | // add this \n onto the end. So if we're printing just a \n by itself, 56 | // just don't do anything. 57 | if (!(Length == 1 && outc[0] == '\n')) 58 | { 59 | std::wstring wfmt = Util::Widen(outc); 60 | const bool doInjectedNext = UnrealSDK::gInjectedCallNext; 61 | UnrealSDK::DoInjectedCallNext(); 62 | UnrealSDK::gameConsole->OutputText(FString(wfmt.c_str())); 63 | if (doInjectedNext) 64 | UnrealSDK::DoInjectedCallNext(); 65 | } 66 | } 67 | } 68 | 69 | void LogW(wchar_t* Formatted, const signed int Length) 70 | { 71 | char* output = (char *)calloc(Length + 1, sizeof(char)); 72 | wcstombs(output, Formatted, Length); 73 | Log(output, 0); 74 | } 75 | 76 | void LogPy(std::string formatted) 77 | { 78 | Log(formatted.c_str(), 0); 79 | } 80 | 81 | void LogF(const char* fmt, ...) 82 | { 83 | va_list args; 84 | va_start(args, fmt); 85 | std::string formatted = Util::FormatInternal(fmt, args); 86 | va_end(args); 87 | 88 | Log(formatted.c_str(), formatted.length()); 89 | } 90 | 91 | enum LogLevel 92 | { 93 | DEBUG, 94 | INFO, 95 | WARNING, 96 | EXCEPTION, 97 | CRITICAL 98 | }; 99 | 100 | LogLevel Level = WARNING; 101 | 102 | void LogD(const char* fmt, ...) 103 | { 104 | if (Level == DEBUG) 105 | { 106 | va_list args; 107 | va_start(args, fmt); 108 | std::string formatted = "[DEBUG] " + Util::FormatInternal(fmt, args); 109 | va_end(args); 110 | 111 | Log(formatted.c_str(), formatted.length()); 112 | } 113 | } 114 | 115 | void SetLoggingLevel(const char* NewLevel) 116 | { 117 | std::string str = NewLevel; 118 | std::transform(str.begin(), str.end(), str.begin(), toupper); 119 | if (str == "DEBUG") 120 | { 121 | Level = DEBUG; 122 | } 123 | else if (str == "INFO") 124 | { 125 | Level = INFO; 126 | } 127 | else if (str == "WARNING") 128 | { 129 | Level = WARNING; 130 | } 131 | else if (str == "EXCEPTION") 132 | { 133 | Level = EXCEPTION; 134 | } 135 | else if (str == "CRITICAL") 136 | { 137 | Level = CRITICAL; 138 | } 139 | else 140 | { 141 | LogF("Unknown logging level '%s'\n", NewLevel); 142 | } 143 | } 144 | 145 | void InitializeExtern() 146 | { 147 | BOOL result = AllocConsole(); 148 | if (result) 149 | { 150 | gLogToExternalConsole = true; 151 | } 152 | } 153 | 154 | // Everything else can fail, but InitializeFile must work. 155 | void InitializeFile(const std::wstring& fileName) 156 | { 157 | gLogFile = CreateFile(fileName.c_str(), GENERIC_WRITE, FILE_SHARE_READ, nullptr, CREATE_ALWAYS, 158 | FILE_ATTRIBUTE_NORMAL, nullptr); 159 | if (gLogFile == INVALID_HANDLE_VALUE) 160 | { 161 | std::string errMsg = Util::Format("Failed to initialize log file (INVALID_HANDLE_VALUE, LastError = %d)", 162 | GetLastError()); 163 | throw FatalSDKException(1000, errMsg); 164 | } 165 | 166 | gLogToFile = true; 167 | } 168 | 169 | void PrintLogHeader() 170 | { 171 | LogF("======== UnrealEngine PythonSDK Loaded (Version %d) ========\n", UnrealSDK::EngineVersion); 172 | } 173 | 174 | void Cleanup() 175 | { 176 | if (gLogFile != INVALID_HANDLE_VALUE) 177 | { 178 | FlushFileBuffers(gLogFile); 179 | CloseHandle(gLogFile); 180 | } 181 | } 182 | } 183 | -------------------------------------------------------------------------------- /src/PackageFix.cpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "stdafx.h" 3 | #include "UnrealSDK.h" 4 | #include "PackageFix.h" 5 | #include "gamedefines.h" 6 | 7 | namespace UnrealSDK 8 | { 9 | bool GIsLoadingUDKPackage = false; 10 | DWORD dwTextureFixReturn; 11 | 12 | void WriteJMPHook(unsigned char* hookLocation, unsigned char* jumpTo, int hookLength) 13 | { 14 | DWORD dwOldProtect; 15 | VirtualProtect(hookLocation, hookLength, PAGE_EXECUTE_READWRITE, &dwOldProtect); 16 | 17 | // Write jmp opcode 18 | *hookLocation = 0xE9; 19 | 20 | // Write jump offset 21 | DWORD dwRelAddr = (DWORD)(jumpTo - hookLocation - 5); 22 | *((DWORD*)(hookLocation + 0x1)) = dwRelAddr; 23 | 24 | // Overwrite the rest with nop 25 | for (int i = 5; i < hookLength; i++) 26 | *(hookLocation + i) = 0x90; 27 | 28 | DWORD dwDummy; 29 | VirtualProtect(hookLocation, hookLength, dwOldProtect, &dwDummy); 30 | } 31 | 32 | __declspec(naked) void hkTexture2DSerialize() 33 | { 34 | __asm 35 | { 36 | pushad 37 | pushfd 38 | } 39 | 40 | // If we're loading a UDK package, skip over the source art data 41 | FixTextureLoad(); 42 | 43 | __asm 44 | { 45 | popfd 46 | popad 47 | 48 | mov eax, [esi + 16] 49 | test eax, eax 50 | 51 | jmp[dwTextureFixReturn] 52 | } 53 | } 54 | 55 | void InitializePackageFix() 56 | { 57 | dwTextureFixReturn = (DWORD)pTextureFixLocation + 5; 58 | WriteJMPHook((unsigned char*)pTextureFixLocation, (unsigned char*)hkTexture2DSerialize, 5); 59 | } 60 | 61 | void SetIsLoadingUDKPackage(bool loadingUDKPkg) 62 | { 63 | GIsLoadingUDKPackage = loadingUDKPkg; 64 | } 65 | } -------------------------------------------------------------------------------- /src/Settings.cpp: -------------------------------------------------------------------------------- 1 | #include "stdafx.h" 2 | #include "Settings.h" 3 | #include "Exceptions.h" 4 | #include "Logging.h" 5 | 6 | namespace Settings 7 | { 8 | std::wstring gBinPath; 9 | bool gDeveloperMode; 10 | bool gDisableAntiDebug; 11 | 12 | void Initialize(wchar_t* BinPath/*LauncherStruct* args*/) 13 | { 14 | /* 15 | if (args == nullptr || args->BinPath == nullptr) 16 | { 17 | throw FatalSDKException(6000, "Launcher settings struct was invalid, did you use the launcher?"); 18 | } 19 | */ 20 | 21 | gBinPath = BinPath; //args->BinPath; 22 | gDeveloperMode = true; //args->DeveloperMode; 23 | gDisableAntiDebug = true; // args->DisableAntiDebug; 24 | } 25 | 26 | std::wstring GetLogFilePath() 27 | { 28 | return GetBinFile(L"python-sdk.log"); 29 | } 30 | 31 | std::wstring GetConfigFile() 32 | { 33 | return GetBinFile(L"python-sdk.cfg"); 34 | } 35 | 36 | std::wstring GetBinFile(const std::wstring& Filename) 37 | { 38 | std::wstring newPath = gBinPath + Filename; 39 | return newPath; 40 | } 41 | 42 | std::wstring GetTextureFile(const std::wstring& Filename) 43 | { 44 | std::wstring newPath = gBinPath + L"textures\\" + Filename; 45 | return newPath; 46 | } 47 | 48 | std::wstring GetPythonFile(const std::wstring& Filename) 49 | { 50 | std::wstring newPath = gBinPath + L"Mods\\" + Filename; 51 | return newPath; 52 | } 53 | 54 | bool DeveloperModeEnabled() 55 | { 56 | return gDeveloperMode; 57 | } 58 | 59 | bool DisableAntiDebug() 60 | { 61 | return gDisableAntiDebug; 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/Util.cpp: -------------------------------------------------------------------------------- 1 | #define WIN32_LEAN_AND_MEAN 2 | #include "stdafx.h" 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include "Util.h" 9 | #include "Logging.h" 10 | 11 | //From McSimp's Borderlands2SDK 12 | 13 | namespace Util 14 | { 15 | static bool gIsInit = false; 16 | 17 | //Get top window 18 | static HWND gTopWnd = nullptr; 19 | 20 | void Initialize() 21 | { 22 | if (gIsInit) 23 | return; 24 | gIsInit = true; 25 | } 26 | 27 | struct EnumWindowsCallbackArgs 28 | { 29 | EnumWindowsCallbackArgs(DWORD p) : pid(p) 30 | { 31 | } 32 | 33 | const DWORD pid; 34 | std::vector handles; 35 | }; 36 | 37 | static BOOL CALLBACK EnumWindowsCallback(HWND hnd, LPARAM lParam) 38 | { 39 | EnumWindowsCallbackArgs* args = (EnumWindowsCallbackArgs *)lParam; 40 | DWORD windowPID; 41 | (void)GetWindowThreadProcessId(hnd, &windowPID); 42 | if (windowPID == args->pid) 43 | { 44 | args->handles.push_back(hnd); 45 | } 46 | return TRUE; 47 | } 48 | 49 | HWND getToplevelWindows() 50 | { 51 | if (gTopWnd) 52 | return gTopWnd; 53 | EnumWindowsCallbackArgs args(GetCurrentProcessId()); 54 | if (EnumWindows(&EnumWindowsCallback, (LPARAM)&args) == FALSE) 55 | { 56 | return nullptr; 57 | } 58 | return gTopWnd = args.handles[0]; 59 | } 60 | 61 | DWORD GetMainThreadId(DWORD DwPid) 62 | { 63 | LPVOID lpTid; 64 | _asm 65 | { 66 | mov eax, fs:[18h] 67 | add eax, 36 68 | mov[lpTid], eax 69 | } 70 | HANDLE hProcess = OpenProcess(PROCESS_VM_READ, FALSE, DwPid); 71 | if (hProcess == nullptr) 72 | return NULL; 73 | DWORD dwTid; 74 | if (ReadProcessMemory(hProcess, lpTid, &dwTid, sizeof(dwTid), nullptr) == FALSE) 75 | { 76 | CloseHandle(hProcess); 77 | return NULL; 78 | } 79 | CloseHandle(hProcess); 80 | return dwTid; 81 | } 82 | 83 | HANDLE GetMainThreadHandle(const DWORD DwPid, const DWORD DwDesiredAccess) 84 | { 85 | const DWORD dwTid = GetMainThreadId(DwPid); 86 | if (dwTid == FALSE) 87 | return nullptr; 88 | return OpenThread(DwDesiredAccess, FALSE, dwTid); 89 | } 90 | 91 | std::string FormatInternal(const char* Fmt, const va_list Args) 92 | { 93 | std::string str; 94 | 95 | const int buffSize = _vscprintf(Fmt, Args) + 1; 96 | 97 | if (buffSize <= 1) 98 | return str; 99 | 100 | char* szBuff = (char *)calloc(buffSize, sizeof(char)); 101 | 102 | vsprintf_s(szBuff, buffSize, Fmt, Args); 103 | 104 | szBuff[buffSize - 1] = 0; 105 | 106 | str = szBuff; 107 | delete[] szBuff; 108 | 109 | return str; 110 | } 111 | 112 | std::string Format(const char* fmt, ...) 113 | { 114 | va_list args; 115 | va_start(args, fmt); 116 | std::string formatted = FormatInternal(fmt, args); 117 | va_end(args); 118 | 119 | return formatted; 120 | } 121 | 122 | std::wstring FormatInternal(const wchar_t* fmt, va_list args) 123 | { 124 | std::wstring str; 125 | 126 | const int buffSize = _vscwprintf(fmt, args) + 1; 127 | 128 | if (buffSize <= 1) 129 | return str; 130 | 131 | wchar_t* szBuff = (wchar_t *)calloc(buffSize, sizeof(wchar_t)); 132 | 133 | vswprintf_s(szBuff, buffSize, fmt, args); 134 | 135 | szBuff[buffSize - 1] = 0; 136 | 137 | str = szBuff; 138 | free(szBuff); 139 | 140 | return str; 141 | } 142 | 143 | std::wstring Format(const wchar_t* fmt, ...) 144 | { 145 | va_list args; 146 | va_start(args, fmt); 147 | std::wstring formatted = FormatInternal(fmt, args); 148 | va_end(args); 149 | 150 | return formatted; 151 | } 152 | 153 | // TODO: Benchmarking and whatnot to see how these perform 154 | std::wstring Widen(const std::string& Input) 155 | { 156 | std::wstring out; 157 | out.assign(Input.begin(), Input.end()); 158 | return out; 159 | } 160 | 161 | std::string Narrow(const std::wstring& Input) 162 | { 163 | std::string out; 164 | out.assign(Input.begin(), Input.end()); 165 | return out; 166 | } 167 | 168 | void Popup(const std::wstring& StrName, const std::wstring& StrText) 169 | { 170 | MessageBox(nullptr, StrText.c_str(), StrName.c_str(), MB_OK | MB_ICONASTERISK); 171 | } 172 | 173 | void CloseGame() 174 | { 175 | TerminateProcess(GetCurrentProcess(), 1); 176 | } 177 | 178 | // This will convert a string like "Hello World" to "48 65 6C 6C 6F 20 57 6F 72 6C 64" 179 | // Taken mostly from http://stackoverflow.com/questions/3381614/c-convert-string-to-hexadecimal-and-vice-versa 180 | std::string StringToHex(const char* Input, const size_t Len) 181 | { 182 | static const char* const lut = "0123456789ABCDEF"; 183 | 184 | std::string output; 185 | output.reserve((2 * Len) + Len); 186 | for (size_t i = 0; i < Len; ++i) 187 | { 188 | const unsigned char c = Input[i]; 189 | output.push_back(lut[c >> 4]); 190 | output.push_back(lut[c & 15]); 191 | output.push_back(' '); 192 | } 193 | output.resize(output.size() - 1); // Remove that last space 194 | 195 | return output; 196 | } 197 | 198 | int WaitForModules(std::int32_t Timeout, const std::initializer_list& Modules) 199 | { 200 | bool signaled[32] = {false}; 201 | bool success = false; 202 | 203 | std::uint32_t totalSlept = 0; 204 | 205 | if (Timeout == 0) 206 | { 207 | for (auto& mod : Modules) 208 | { 209 | if (GetModuleHandleW(std::data(mod)) == nullptr) 210 | return WAIT_TIMEOUT; 211 | } 212 | return WAIT_OBJECT_0; 213 | } 214 | 215 | if (Timeout < 0) 216 | Timeout = INT32_MAX; 217 | 218 | while (true) 219 | { 220 | for (auto i = 0u; i < Modules.size(); ++i) 221 | { 222 | auto& module = *(Modules.begin() + i); 223 | if (!signaled[i] && GetModuleHandleW(std::data(module)) != nullptr) 224 | { 225 | signaled[i] = true; 226 | 227 | // 228 | // Checks if all modules are signaled 229 | // 230 | bool done = true; 231 | for (auto j = 0u; j < Modules.size(); ++j) 232 | { 233 | if (!signaled[j]) 234 | { 235 | done = false; 236 | break; 237 | } 238 | } 239 | if (done) 240 | { 241 | success = true; 242 | goto exit; 243 | } 244 | } 245 | } 246 | if (totalSlept > std::uint32_t(Timeout)) 247 | { 248 | break; 249 | } 250 | Sleep(10); 251 | totalSlept += 10; 252 | } 253 | 254 | exit: 255 | return success ? WAIT_OBJECT_0 : WAIT_TIMEOUT; 256 | } 257 | } 258 | -------------------------------------------------------------------------------- /src/include/AntiDebug.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #ifndef ANTI_DEBUG_H 3 | #define ANTI_DEBUG_H 4 | 5 | namespace UnrealSDK 6 | { 7 | void HookAntiDebug(); 8 | } 9 | 10 | #endif 11 | -------------------------------------------------------------------------------- /src/include/CHookManager.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #ifndef C_HOOK_MANAGER_H 3 | #define C_HOOK_MANAGER_H 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | class CHookManager 12 | { 13 | public: 14 | typedef std::map> tHookMap; 15 | typedef std::pair> tHookPair; 16 | 17 | public: 18 | std::map hooks; 19 | std::string debugName; 20 | 21 | CHookManager() : debugName("Unknown") 22 | { 23 | } 24 | 25 | CHookManager(std::string DebugName) : debugName(std::move(DebugName)) 26 | { 27 | } 28 | 29 | void Register(const std::string& FuncName, const std::string& HookName, 30 | const std::function 31 | & FuncHook); 32 | bool Remove(const std::string& FuncName, const std::string& HookName); 33 | bool ProcessHooks(const std::string& FuncName, const UObject* Caller, const UFunction* Func, const FStruct* Params); 34 | bool ProcessHooks(UObject* Caller, FFrame& Stack, void* Result, UFunction* Function); 35 | bool HasHook(UObject* Caller, UFunction* Func); 36 | }; 37 | 38 | #endif 39 | -------------------------------------------------------------------------------- /src/include/CPythonInterface.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #ifndef CPYTHONINTERFACE_H 3 | #define CPYTHONINTERFACE_H 4 | 5 | enum PythonStatus 6 | { 7 | PYTHON_OK, 8 | PYTHON_MODULE_ERROR 9 | }; 10 | 11 | class CPythonInterface 12 | { 13 | public: 14 | CPythonInterface(); 15 | ~CPythonInterface(); 16 | 17 | PythonStatus InitializeModules(); 18 | int RunString(const char* string); 19 | void CallShutdownFuncs(); 20 | int DoFile(const char* filename); 21 | int DoString(const char* command); 22 | 23 | pybind11::object GetPythonNamespace(); 24 | 25 | private: 26 | void InitializeState(); 27 | void InitLogging(); 28 | void CleanupState(); 29 | void SetSDKValues(); 30 | void SetPaths(); 31 | int DoFileAbsolute(const char* path); 32 | 33 | pybind11::object m_mainNamespace; 34 | std::string m_PythonPath; 35 | std::string m_StdoutBuffer; 36 | std::string m_StderrBuffer; 37 | bool m_modulesInitialized; 38 | }; 39 | 40 | #endif 41 | -------------------------------------------------------------------------------- /src/include/CSigScan.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #define WIN32_LEAN_AND_MEAN 3 | #include 4 | 5 | struct MemorySignature; 6 | 7 | class CSigScan 8 | { 9 | private: 10 | char* m_pModuleBase; 11 | size_t m_moduleLen; 12 | 13 | public: 14 | HMODULE m_moduleHandle; 15 | CSigScan(const wchar_t* moduleName); 16 | 17 | void* Scan(const MemorySignature& sigStruct); 18 | void* Scan(const char* sig, const char* mask, int sigLength); 19 | void* Scan(const char* sig, const char* mask); 20 | }; 21 | -------------------------------------------------------------------------------- /src/include/CSimpleDetour.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #ifndef CSIMPLEDETOUR_H 3 | #define CSIMPLEDETOUR_H 4 | 5 | #define WIN32_LEAN_AND_MEAN 6 | #include 7 | #include "detours/detours.h" 8 | 9 | class CSimpleDetour 10 | { 11 | public: 12 | CSimpleDetour(void** old, void* replacement); 13 | 14 | void Attach(); 15 | void Detach(); 16 | private: 17 | void** m_fnOld; 18 | void* m_fnReplacement; 19 | 20 | bool m_bAttached; 21 | }; 22 | 23 | #define SETUP_SIMPLE_DETOUR(name, old, replacement) \ 24 | CSimpleDetour name(&(PVOID&)old, (void *)(&(void * &)replacement)) 25 | 26 | #define SETUP_DETOUR_FUNCTION( ret, conv, name, params ) \ 27 | ret conv name##_H params; \ 28 | ret ( conv *name##_T ) params = name; \ 29 | CSimpleDetour *Detour_##name = new CSimpleDetour( &(void * &)name##_T, (void *)(&(void * &)name##_H) ); \ 30 | ret conv name##_H params 31 | 32 | #define SETUP_DETOUR_FUNCTION_LATE( ret, conv, name, params ) \ 33 | ret conv name##_H params; \ 34 | ret ( conv *name##_T ) params = NULL; \ 35 | CSimpleDetour *Detour_##name = NULL; \ 36 | ret conv name##_H params 37 | 38 | #define SETUP_DETOUR_LATE( name ) \ 39 | Detour_##name = new CSimpleDetour( &(void * &)name##_T, (void *)(&(void * &)name##_H) ) 40 | 41 | #define SETUP_DETOUR_EXTERN( ret, conv, name, params ) \ 42 | extern ret ( conv *name##_T ) params; \ 43 | extern CSimpleDetour *Detour_##name 44 | 45 | #define SETUP_DETOUR_TRAMP( ret, conv, name, params ) \ 46 | ret ( conv *name##_T ) params = NULL; 47 | #endif // !CSIMPLEDETOUR_H_ 48 | -------------------------------------------------------------------------------- /src/include/Exceptions.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #ifndef EXCEPTIONS_H 3 | #define EXCEPTIONS_H 4 | 5 | #include 6 | #include 7 | 8 | class FatalSDKException : public std::runtime_error 9 | { 10 | private: 11 | int m_errorCode; 12 | 13 | public: 14 | FatalSDKException(int errorCode, const std::string& errorStr) 15 | : std::runtime_error(errorStr), 16 | m_errorCode(errorCode) 17 | { 18 | } 19 | 20 | int GetErrorCode() 21 | { 22 | return m_errorCode; 23 | } 24 | }; 25 | 26 | #endif 27 | -------------------------------------------------------------------------------- /src/include/Exports.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #ifndef EXPORTS_H 3 | #define EXPORTS_H 4 | 5 | #define FFI_EXPORT extern "C" __declspec(dllexport) 6 | 7 | #endif 8 | -------------------------------------------------------------------------------- /src/include/Games.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "stdafx.h" 4 | #include 5 | #include "MemorySignature.h" 6 | 7 | static std::map bl2_signatures{{ 8 | { 9 | "GNames", { 10 | "\x00\x00\x00\x00\x83\x3C\x81\x00\x74\x5C", 11 | "????xxxxxx", 12 | 10 13 | }}, 14 | { 15 | "GObjects", { 16 | "\x00\x00\x00\x00\x8B\x04\xB1\x8B\x40\x08", 17 | "????xxxxxx", 18 | 10 19 | }}, 20 | { 21 | "SetCommand", { 22 | "\x83\xC4\x0C\x85\xC0\x75\x1A\x6A\x01\x8D", 23 | "xxxxxxxxxx", 24 | 10 25 | }}, 26 | { 27 | "ProcessEvent", { 28 | "\x55\x8B\xEC\x6A\xFF\x68\x00\x00\x00\x00\x64\xA1\x00\x00\x00\x00\x50\x83\xEC\x50\xA1\x00\x00\x00\x00\x33\xC5\x89\x45\xF0\x53\x56\x57\x50\x8D\x45\xF4\x64\xA3\x00\x00\x00\x00\x8B\xF1\x89\x75\xEC", 29 | "xxxxxx????xx????xxxxx????xxxxxxxxxxxxxx????xxxxx", 30 | 48 31 | }}, 32 | { 33 | "CallFunction", { 34 | "\x55\x8B\xEC\x6A\xFF\x68\x00\x00\x00\x00\x64\xA1\x00\x00\x00\x00\x50\x81\xEC\x00\x00\x00\x00\xA1\x00\x00\x00\x00\x33\xC5\x89\x45\xF0\x53\x56\x57\x50\x8D\x45\xF4\x64\xA3\x00\x00\x00\x00\x8B\x7D\x10\x8B\x45\x0C", 35 | "xxxxxx????xx????xxx????x????xxxxxxxxxxxxxx????xxxxxx", 36 | 52 37 | }}, 38 | { 39 | "FrameStep", { 40 | "\x55\x8B\xEC\x8B\x41\x18\x0F\xB6\x10", 41 | "xxxxxxxxx", 42 | 9 43 | }}, 44 | { 45 | "StaticConstructor", { 46 | "\x55\x8B\xEC\x6A\xFF\x68\x00\x00\x00\x00\x64\xA1\x00\x00\x00\x00\x50\x83\xEC\x10\x53\x56\x57\xA1\x00\x00\x00\x00\x33\xC5\x50\x8D\x45\xF4\x64\xA3\x00\x00\x00\x00\x8B\x7D\x08\x8A\x87", 47 | "xxxxxx????xx????xxxxxxxx????xxxxxxxx????xxxxx", 48 | 45 49 | }}, 50 | { 51 | "LoadPackage", { 52 | "\x55\x8B\xEC\x6A\xFF\x68\x00\x00\x00\x00\x64\xA1\x00\x00\x00\x00\x50\x83\xEC\x68\xA1\x00\x00\x00\x00\x33\xC5\x89\x45\xEC", 53 | "xxxxxx????xx????xxxxx????xxxxx", 54 | 30 55 | }}, 56 | { 57 | "GMalloc", { 58 | "\x00\x00\x00\x00\xFF\xD7\x83\xC4\x04\x89\x45\xE4", 59 | "????xxxxxxxx", 60 | 12 61 | }}, 62 | { 63 | "FNameInit", { 64 | "\x55\x8B\xEC\x6A\xFF\x68\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x50\x81\xEC\x9C\x0C", 65 | "xxxxxx??????xxxxxxxxx", 66 | 21 67 | }}, 68 | { 69 | "GetDefaultObject", { 70 | "\x55\x8B\xEC\x56\x8B\xF1\x83\xBE\x00\x01\x00\x00\x00\x57\x0F\x85", 71 | "xxxxxxxx?xxxxxxx", 72 | 16 73 | }} 74 | }}; 75 | 76 | static std::map tps_signatures{ { 77 | { 78 | "GNames", { 79 | "\x00\x00\x00\x00\x8B\x04\xB1\x5E\x5D\xC3\x8B\x15", 80 | "????xxxxxxxx", 81 | 12 82 | }}, 83 | {"GObjects", bl2_signatures["GObjects"]}, 84 | {"SetCommand", bl2_signatures["SetCommand"]}, 85 | {"ProcessEvent", bl2_signatures["ProcessEvent"]}, 86 | {"CallFunction", bl2_signatures["CallFunction"]}, 87 | {"FrameStep", bl2_signatures["FrameStep"]}, 88 | {"StaticConstructor", bl2_signatures["StaticConstructor"]}, 89 | {"LoadPackage", bl2_signatures["LoadPackage"]}, 90 | {"GMalloc", bl2_signatures["GMalloc"]}, 91 | {"FNameInit", bl2_signatures["FNameInit"]}, 92 | {"GetDefaultObject", bl2_signatures["GetDefaultObject"]}, 93 | }}; 94 | 95 | 96 | static std::map> game_signature_map{ 97 | {"Borderlands2", bl2_signatures}, 98 | {"BorderlandsPreSequel", tps_signatures}, 99 | {"TinyTina", tps_signatures} 100 | }; 101 | 102 | static std::map bl2_object_map{ 103 | {"ConsoleObjectType", "WillowConsole"}, 104 | {"ConsoleObjectName", "Transient.WillowGameEngine_0:WillowGameViewportClient_0.WillowConsole_0"}, 105 | {"EngineObjectType", "WillowGameEngine"}, 106 | {"EngineObjectName", "Transient.WillowGameEngine"}, 107 | {"EngineFullName", "WillowGameEngine Transient.WillowGameEngine"}, 108 | {"PostRenderFunction", "WillowGame.WillowGameViewportClient.PostRender"} 109 | }; 110 | 111 | static std::map> game_object_map{ 112 | {"Borderlands2", bl2_object_map}, 113 | {"BorderlandsPreSequel", bl2_object_map}, 114 | {"TinyTina", bl2_object_map} 115 | }; -------------------------------------------------------------------------------- /src/include/Logging.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #ifndef LOGGING_H 4 | #define LOGGING_H 5 | #include 6 | 7 | namespace Logging 8 | { 9 | void Log(const char* Formatted, int Length = 0); 10 | void LogW(wchar_t*, int); 11 | void LogPy(std::string formatted); 12 | void LogF(const char* szFmt, ...); 13 | void LogD(const char* szFmt, ...); 14 | void SetLoggingLevel(const char* NewLevel); 15 | void InitializeExtern(); 16 | void InitializeFile(const std::wstring& fileName); 17 | void PrintLogHeader(); 18 | void Cleanup(); 19 | } 20 | 21 | #endif 22 | -------------------------------------------------------------------------------- /src/include/MemoryDebug.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #ifndef MEMORYDEBUG_H 3 | #define MEMORYDEBUG_H 4 | 5 | namespace UnrealSDK 6 | { 7 | void HookMemoryAllocator(); 8 | } 9 | 10 | #endif 11 | -------------------------------------------------------------------------------- /src/include/MemorySignature.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #ifndef MEMORYSIGNATURE_H 3 | #define MEMORYSIGNATURE_H 4 | 5 | struct MemorySignature 6 | { 7 | const char* Sig; 8 | const char* Mask; 9 | int Length; 10 | }; 11 | 12 | #endif 13 | -------------------------------------------------------------------------------- /src/include/Settings.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #ifndef SETTINGS_H 3 | #define SETTINGS_H 4 | 5 | #include 6 | #include 7 | 8 | struct LauncherStruct 9 | { 10 | bool DisableAntiDebug; 11 | bool LogAllCalls; 12 | bool DisableCrashRpt; 13 | bool DeveloperMode; 14 | const LPWSTR BinPath; 15 | }; 16 | 17 | namespace Settings 18 | { 19 | void Initialize(wchar_t* BinPath/* LauncherStruct* args*/); 20 | std::wstring GetLogFilePath(); 21 | std::wstring GetBinFile(const std::wstring& Filename); 22 | std::wstring GetTextureFile(const std::wstring& Filename); 23 | std::wstring GetPythonFile(const std::wstring& Filename); 24 | bool DeveloperModeEnabled(); 25 | bool DisableAntiDebug(); 26 | } 27 | 28 | #endif 29 | -------------------------------------------------------------------------------- /src/include/Signatures.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #ifndef SIGNATURES_H 3 | #define SIGNATURES_H 4 | 5 | #include "MemorySignature.h" 6 | 7 | namespace UnrealSDK 8 | { 9 | namespace Signatures 10 | { 11 | MemorySignature GObjects{}; 12 | MemorySignature GNames{}; 13 | MemorySignature ProcessEvent{}; 14 | MemorySignature CallFunction{}; 15 | MemorySignature FrameStep{}; 16 | MemorySignature StaticConstructor{}; 17 | MemorySignature LoadPackage{}; 18 | MemorySignature GMalloc{}; 19 | MemorySignature FNameInit{}; 20 | MemorySignature GetDefaultObject{}; 21 | MemorySignature SetCommand{}; 22 | 23 | void InitSignatures(std::string ExeName) 24 | { 25 | std::map SignatureMap = game_signature_map[ExeName]; 26 | GObjects = SignatureMap["GObjects"]; 27 | GNames = SignatureMap["GNames"]; 28 | ProcessEvent = SignatureMap["ProcessEvent"]; 29 | CallFunction = SignatureMap["CallFunction"]; 30 | FrameStep = SignatureMap["FrameStep"]; 31 | StaticConstructor = SignatureMap["StaticConstructor"]; 32 | LoadPackage = SignatureMap["LoadPackage"]; 33 | GMalloc = SignatureMap["GMalloc"]; 34 | FNameInit = SignatureMap["FNameInit"]; 35 | GetDefaultObject = SignatureMap["GetDefaultObject"]; 36 | SetCommand = SignatureMap["SetCommand"]; 37 | } 38 | } 39 | } 40 | 41 | #endif 42 | -------------------------------------------------------------------------------- /src/include/TypeMap.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "stdafx.h" 4 | #include 5 | 6 | static std::map uobject_type_map{ 7 | {"ArrayProperty", &typeid(UArrayProperty)}, 8 | {"AttributeModifier", &typeid(UAttributeModifier)}, 9 | {"BoolProperty", &typeid(UBoolProperty)}, 10 | {"ByteAttributeProperty", &typeid(UByteAttributeProperty)}, 11 | {"ByteProperty", &typeid(UByteProperty)}, 12 | {"Class", &typeid(UClass)}, 13 | {"ClassProperty", &typeid(UClassProperty)}, 14 | {"Commandlet", &typeid(UCommandlet)}, 15 | {"Component", &typeid(UComponent)}, 16 | {"ComponentProperty", &typeid(UComponentProperty)}, 17 | {"Const", &typeid(UConst)}, 18 | {"DelegateProperty", &typeid(UDelegateProperty)}, 19 | {"DistributionFloat", &typeid(UDistributionFloat)}, 20 | {"DistributionVector", &typeid(UDistributionVector)}, 21 | {"Enum", &typeid(UEnum)}, 22 | {"Exporter", &typeid(UExporter)}, 23 | {"Factory", &typeid(UFactory)}, 24 | {"Field", &typeid(UField)}, 25 | {"FloatAttributeProperty", &typeid(UFloatAttributeProperty)}, 26 | {"FloatProperty", &typeid(UFloatProperty)}, 27 | {"Function", &typeid(UFunction)}, 28 | {"HelpCommandlet", &typeid(UHelpCommandlet)}, 29 | {"IntAttributeProperty", &typeid(UIntAttributeProperty)}, 30 | {"Interface", &typeid(UInterface)}, 31 | {"InterfaceProperty", &typeid(UInterfaceProperty)}, 32 | {"IntProperty", &typeid(UIntProperty)}, 33 | {"Linker", &typeid(ULinker)}, 34 | {"LinkerSave", &typeid(ULinkerSave)}, 35 | {"MapProperty", &typeid(UMapProperty)}, 36 | {"MetaData", &typeid(UMetaData)}, 37 | {"NameProperty", &typeid(UNameProperty)}, 38 | {"Object", &typeid(UObject)}, 39 | {"ObjectProperty", &typeid(UObjectProperty)}, 40 | {"ObjectRedirector", &typeid(UObjectRedirector)}, 41 | {"ObjectSerializer", &typeid(UObjectSerializer)}, 42 | {"Package", &typeid(UPackage)}, 43 | {"PackageMap", &typeid(UPackageMap)}, 44 | {"Property", &typeid(UProperty)}, 45 | {"ScriptStruct", &typeid(UScriptStruct)}, 46 | {"State", &typeid(UState)}, 47 | {"StrProperty", &typeid(UStrProperty)}, 48 | {"Struct", &typeid(UStruct)}, 49 | {"StructProperty", &typeid(UStructProperty)}, 50 | {"Subsystem", &typeid(USubsystem)}, 51 | {"System", &typeid(USystem)}, 52 | {"TextBuffer", &typeid(UTextBuffer)}, 53 | {"TextBufferFactory", &typeid(UTextBufferFactory)} 54 | }; 55 | -------------------------------------------------------------------------------- /src/include/UnrealSDK.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #ifndef UnrealSDK_H 3 | #define UnrealSDK_H 4 | 5 | #include 6 | #include 7 | #include "CPythonInterface.h" 8 | #include 9 | 10 | class UObject; 11 | class UFunction; 12 | class UClass; 13 | class UPackage; 14 | class UPlayer; 15 | class UConsole; 16 | class CHookManager; 17 | 18 | struct FFrame; 19 | struct FName; 20 | struct FOutputDevice; 21 | struct FArchive; 22 | struct FStruct; 23 | 24 | namespace UnrealSDK 25 | { 26 | typedef void (__thiscall *tProcessEvent)(UObject*, UFunction*, void*, void*); 27 | typedef int (tUnrealEH)(unsigned int, struct _EXCEPTION_POINTERS*); 28 | typedef void (__thiscall *tCallFunction)(UObject*, FFrame&, void*, UFunction*); 29 | typedef void (__thiscall *tFrameStep)(FFrame*, UObject*, void*); 30 | // http://api.unrealengine.com/INT/API/Runtime/CoreUObject/UObject/StaticConstructObject_Internal/index.html 31 | typedef UObject* (*tStaticConstructObject)(UClass* Class, UObject* InOuter, FName Name, unsigned int SetFlags, 32 | unsigned int InternalSetFlags, UObject* InTemplate, FOutputDevice* Error, 33 | void* InstanceGraph, int AssumeTemplateIsArchetype); 34 | typedef UPackage* (*tLoadPackage)(UPackage* Outer, const wchar_t* Filename, DWORD Flags); 35 | typedef FArchive& (__thiscall *tByteOrderSerialize)(FArchive* Ar, void* V, int Length); 36 | 37 | typedef char*(__thiscall *tFNameInitOld)(FName* Out, wchar_t* Src, int InNumber, int FindType, int SplitName, 38 | int Unk1); 39 | typedef void (__thiscall *tFNameInitNew)(FName* Out, wchar_t* Src, int InNumber, int FindType, int SplitName); 40 | typedef UObject*(__thiscall *tGetDefaultObject)(UClass*, unsigned int); 41 | 42 | extern tFNameInitOld pFNameInit; 43 | extern tProcessEvent pProcessEvent; 44 | extern tCallFunction pCallFunction; 45 | extern tFrameStep pFrameStep; 46 | extern tStaticConstructObject pStaticConstructObject; 47 | extern tLoadPackage pLoadPackage; 48 | extern tByteOrderSerialize pByteOrderSerialize; 49 | extern tGetDefaultObject pGetDefaultObject; 50 | extern bool gInjectedCallNext; 51 | extern UConsole* gameConsole; 52 | extern bool gCallPostEdit; 53 | 54 | extern std::map ClassMap; 55 | 56 | extern std::map ObjectMap; 57 | 58 | extern CPythonInterface* Python; 59 | extern CHookManager* gHookManager; 60 | 61 | extern int EngineVersion; 62 | extern int ChangelistNumber; 63 | 64 | extern class UObject* gEngine; 65 | 66 | extern void**** pGMalloc; 67 | 68 | void LogAllCalls(bool Enabled); 69 | void DoInjectedCallNext(); 70 | void Initialize(); 71 | void Cleanup(); 72 | void LoadPackage(const char* Filename, DWORD Flags = 0, bool Force = false); 73 | void KeepAlive(UObject* Obj); 74 | UObject* ConstructObject(UClass* Class, UObject* Outer, FName Name, unsigned int SetFlags, 75 | unsigned int InternalSetFlags, UObject* InTemplate, FOutputDevice* Error, 76 | void* InstanceGraph, int AssumeTemplateIsArchetype); 77 | UObject* GetEngine(); 78 | //UObject *LoadTexture(char *Filename, char *TextureName); 79 | 80 | void RegisterHook(const std::string& FuncName, const std::string& HookName, 81 | const std::function& FuncHook); 82 | bool RemoveHook(const std::string& FuncName, const std::string& HookName); 83 | } 84 | 85 | #endif 86 | -------------------------------------------------------------------------------- /src/include/Util.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | //From McSimp's Borderlands2SDK 3 | 4 | #ifndef UTIL_H 5 | #define UTIL_H 6 | #include "stdafx.h" 7 | 8 | #define PATH_SEPARATOR "\\" 9 | 10 | namespace Util 11 | { 12 | std::string Format(const char* fmt, ...); 13 | std::string FormatInternal(const char* Fmt, va_list Args); 14 | std::wstring Format(const wchar_t* fmt, ...); 15 | std::wstring FormatInternal(const wchar_t* fmt, va_list args); 16 | std::wstring Widen(const std::string& Input); 17 | std::string Narrow(const std::wstring& Input); 18 | void Popup(const std::wstring& StrName, const std::wstring& StrText); 19 | void CloseGame(); 20 | std::string StringToHex(const char* Input, size_t Len); 21 | 22 | void Initialize(); 23 | const char* ServerPath(); 24 | const char* LogPath(); 25 | const char* LayoutPath(); 26 | HWND getToplevelWindows(); 27 | DWORD GetMainThreadId(DWORD DwPid); 28 | HANDLE GetMainThreadHandle(DWORD DwPid, DWORD DwDesiredAccess); 29 | 30 | int WaitForModules(std::int32_t Timeout, const std::initializer_list& Modules); 31 | } 32 | 33 | #endif 34 | -------------------------------------------------------------------------------- /src/include/detours/detver.h: -------------------------------------------------------------------------------- 1 | ////////////////////////////////////////////////////////////////////////////// 2 | // 3 | // Common version parameters. 4 | // 5 | // Microsoft Research Detours Package, Version 4.0.1 6 | // 7 | // Copyright (c) Microsoft Corporation. All rights reserved. 8 | // 9 | 10 | #define _USING_V110_SDK71_ 1 11 | #include "winver.h" 12 | #if 0 13 | #include 14 | #include 15 | #else 16 | #ifndef DETOURS_STRINGIFY 17 | #define DETOURS_STRINGIFY(x) DETOURS_STRINGIFY_(x) 18 | #define DETOURS_STRINGIFY_(x) #x 19 | #endif 20 | 21 | #define VER_FILEFLAGSMASK 0x3fL 22 | #define VER_FILEFLAGS 0x0L 23 | #define VER_FILEOS 0x00040004L 24 | #define VER_FILETYPE 0x00000002L 25 | #define VER_FILESUBTYPE 0x00000000L 26 | #endif 27 | #define VER_DETOURS_BITS DETOUR_STRINGIFY(DETOURS_BITS) 28 | -------------------------------------------------------------------------------- /src/include/gamedefines.h: -------------------------------------------------------------------------------- 1 | #ifndef GAMEDEFINES_H 2 | #define GAMEDEFINES_H 3 | 4 | #pragma once 5 | #define WIN32_LEAN_AND_MEAN 6 | #include 7 | #include 8 | #include 9 | #include "Util.h" 10 | #include "stdafx.h" 11 | #include "UnrealSDK.h" 12 | #include "logging.h" 13 | 14 | /* 15 | # ========================================================================================= # 16 | # Structs 17 | # ========================================================================================= # 18 | */ 19 | namespace py = pybind11; 20 | 21 | class PyTArray 22 | { 23 | public: 24 | PyTArray() 25 | { 26 | } 27 | }; 28 | 29 | template 30 | struct TArray 31 | { 32 | public: 33 | T* Data; 34 | unsigned int Count; 35 | unsigned int Max; 36 | 37 | TArray() 38 | { 39 | Data = nullptr; 40 | Count = 0; 41 | Max = 0; 42 | } 43 | 44 | int Num() 45 | { 46 | return this->Count; 47 | } 48 | 49 | T& operator()(int i) 50 | { 51 | return this->Data[i]; 52 | } 53 | 54 | const T& operator()(int i) const 55 | { 56 | return this->Data[i]; 57 | } 58 | }; 59 | 60 | struct FNameEntry 61 | { 62 | unsigned char UnknownData00[0x10]; 63 | char Name[1024]; 64 | }; 65 | 66 | struct FName 67 | { 68 | int Index; 69 | int Number; 70 | 71 | public: 72 | FName() 73 | { 74 | Index = 0; 75 | Number = 0; 76 | }; 77 | 78 | public: 79 | FName(const std::string& FindName) 80 | { 81 | if (UnrealSDK::EngineVersion <= 8631) 82 | ((UnrealSDK::tFNameInitOld)(UnrealSDK::pFNameInit))(this, (wchar_t *)Util::Widen(FindName).c_str(), 0, 1, 1, 0); 83 | else 84 | ((UnrealSDK::tFNameInitNew)(UnrealSDK::pFNameInit))(this, (wchar_t *)Util::Widen(FindName).c_str(), 0, 1, 1); 85 | Logging::LogD("Made FName; Index: %d, Number: %d, Name: %s\n", Index, Number, GetName()); 86 | } 87 | 88 | FName(const std::string& FindName, int number) 89 | { 90 | if (UnrealSDK::EngineVersion <= 8631) 91 | ((UnrealSDK::tFNameInitOld)(UnrealSDK::pFNameInit))(this, (wchar_t *)Util::Widen(FindName).c_str(), number, 1, 1, 92 | 0); 93 | else 94 | ((UnrealSDK::tFNameInitNew)(UnrealSDK::pFNameInit))(this, (wchar_t *)Util::Widen(FindName).c_str(), number, 1, 1); 95 | } 96 | 97 | static TArray* Names() 98 | { 99 | return (TArray*)UnrealSDK::pGNames; 100 | } 101 | 102 | const char* GetName() const 103 | { 104 | if (Index < 0 || Index > Names()->Num()) 105 | return "UnknownName"; 106 | return Names()->Data[Index]->Name; 107 | }; 108 | 109 | bool operator ==(const FName& A) const 110 | { 111 | return Index == A.Index; 112 | } 113 | }; 114 | 115 | struct FString : public TArray 116 | { 117 | FString() 118 | { 119 | }; 120 | 121 | FString(const wchar_t* Other) 122 | { 123 | this->Max = this->Count = Other ? (wcslen(Other) + 1) : 0; 124 | this->Data = (wchar_t *)calloc(this->Count, sizeof(wchar_t)); 125 | 126 | if (this->Count) 127 | wcscpy(this->Data, Other); 128 | }; 129 | 130 | FString(const char* Other) 131 | { 132 | this->Max = this->Count = Other ? (strlen(Other) + 1) : 0; 133 | this->Data = (wchar_t *)calloc(this->Count, sizeof(wchar_t)); 134 | 135 | if (this->Count) 136 | mbstowcs(this->Data, Other, this->Count); 137 | }; 138 | 139 | ~FString() 140 | { 141 | }; 142 | 143 | FString operator =(wchar_t* Other) 144 | { 145 | if (this->Data != Other) 146 | { 147 | this->Max = this->Count = *Other ? (wcslen(Other) + 1) : 0; 148 | 149 | if (this->Count) 150 | this->Data = Other; 151 | } 152 | 153 | return *this; 154 | }; 155 | 156 | char* AsString() 157 | { 158 | if (this->Data == nullptr || this->Count == 0) 159 | return (char *)""; 160 | char* output = (char *)calloc(this->Count + 1, sizeof(char)); 161 | wcstombs(output, this->Data, this->Count); 162 | return output; 163 | } 164 | }; 165 | 166 | struct FScriptDelegate 167 | { 168 | struct FName FunctionName; 169 | class UObject* Object; 170 | }; 171 | 172 | struct FScriptInterface 173 | { 174 | UObject* ObjectPointer; //A pointer to a UObject that implements a native interface. 175 | void* InterfacePointer; 176 | //Pointer to the location of the interface object within the UObject referenced by ObjectPointer. 177 | }; 178 | 179 | struct FOutputDevice 180 | { 181 | void* VfTable; 182 | unsigned long bAllowSuppression; 183 | unsigned long bSuppressEventTag; 184 | unsigned long bAutoEmitLineTerminator; 185 | }; 186 | 187 | struct FWindowsViewport; 188 | 189 | struct FDeferredMessage 190 | { 191 | FWindowsViewport* Viewport; 192 | UINT Message; 193 | WPARAM wParam; 194 | LPARAM lParam; 195 | 196 | struct 197 | { 198 | SHORT LeftControl; 199 | SHORT RightControl; 200 | SHORT LeftShift; 201 | SHORT RightShift; 202 | SHORT Menu; 203 | } KeyStates; 204 | }; 205 | 206 | struct FArchive 207 | { 208 | }; 209 | 210 | struct FArchiveAsync : FArchive 211 | { 212 | unsigned char Unknown[0x9C]; 213 | INT CurrentPos; 214 | }; 215 | 216 | struct ULinkerLoad : FArchive 217 | { 218 | void* VfTable; 219 | unsigned char Unknown[0x498]; 220 | FArchiveAsync* Loader; 221 | }; 222 | 223 | 224 | /* 225 | struct TStringArray : TArray { 226 | struct FString GetItem(int i) { 227 | return ((FString *)Data)[i]; 228 | } 229 | 230 | void SetItem(int i, FString d) { 231 | ((FString *)Data)[i] = d; 232 | } 233 | }; 234 | 235 | struct TObjectArray : TArray { 236 | class UObject *GetItem(int i) { 237 | return ((UObject **)Data)[i]; 238 | } 239 | 240 | void SetItem(int i, UObject * d) { 241 | ((UObject **)Data)[i] = d; 242 | } 243 | }; 244 | 245 | struct TComponentArray : TArray { 246 | class UComponent *GetItem(int i) { 247 | return ((UComponent **)Data)[i]; 248 | } 249 | 250 | void SetItem(int i, UComponent * d) { 251 | ((UComponent **)Data)[i] = d; 252 | } 253 | }; 254 | 255 | struct TClassArray : TArray { 256 | class UClass *GetItem(int i) { 257 | return ((UClass **)Data)[i]; 258 | } 259 | 260 | void SetItem(int i, UClass * d) { 261 | ((UClass **)Data)[i] = d; 262 | } 263 | }; 264 | 265 | struct TNameArray : TArray { 266 | struct FName GetItem(int i) { 267 | return ((FName *)Data)[i]; 268 | } 269 | 270 | void SetItem(int i, FName d) { 271 | ((FName *)Data)[i] = d; 272 | } 273 | }; 274 | 275 | struct TNameEntryArray : TArray { 276 | struct FNameEntry GetItem(int i) { 277 | return ((FNameEntry *)Data)[i]; 278 | } 279 | 280 | void SetItem(int i, FNameEntry d) { 281 | ((FNameEntry *)Data)[i] = d; 282 | } 283 | }; 284 | 285 | struct TIntArray : TArray { 286 | int GetItem(int i) { 287 | return ((int *)Data)[i]; 288 | } 289 | 290 | void SetItem(int i, int d) { 291 | ((int *)Data)[i] = d; 292 | } 293 | }; 294 | 295 | struct TInterfaceArray : TArray { 296 | class UInterface *GetItem(int i) { 297 | return ((UInterface **)Data)[i]; 298 | } 299 | 300 | void SetItem(int i, UInterface *d) { 301 | ((UInterface **)Data)[i] = d; 302 | } 303 | }; 304 | 305 | struct TFloatArray : TArray { 306 | float GetItem(int i) { 307 | return ((float *)Data)[i]; 308 | } 309 | 310 | void SetItem(int i, float d) { 311 | ((float *)Data)[i] = d; 312 | } 313 | }; 314 | 315 | struct TDelegateArray : TArray { 316 | struct FScriptDelegate GetItem(int i) { 317 | return ((FScriptDelegate *)Data)[i]; 318 | } 319 | 320 | void SetItem(int i, FScriptDelegate d) { 321 | ((FScriptDelegate *)Data)[i] = d; 322 | } 323 | }; 324 | 325 | struct TCharArray : TArray { 326 | char GetItem(int i) { 327 | return ((char *)Data)[i]; 328 | } 329 | 330 | void SetItem(int i, char d) { 331 | ((char *)Data)[i] = d; 332 | } 333 | }; 334 | 335 | struct TWCharArray : TArray { 336 | wchar_t GetItem(int i) { 337 | return ((wchar_t *)Data)[i]; 338 | } 339 | 340 | void SetItem(int i, wchar_t d) { 341 | ((wchar_t *)Data)[i] = d; 342 | } 343 | }; 344 | */ 345 | 346 | #endif 347 | -------------------------------------------------------------------------------- /src/include/pybind11/buffer_info.h: -------------------------------------------------------------------------------- 1 | /* 2 | pybind11/buffer_info.h: Python buffer object interface 3 | 4 | Copyright (c) 2016 Wenzel Jakob 5 | 6 | All rights reserved. Use of this source code is governed by a 7 | BSD-style license that can be found in the LICENSE file. 8 | */ 9 | 10 | #pragma once 11 | 12 | #include "detail/common.h" 13 | 14 | NAMESPACE_BEGIN(PYBIND11_NAMESPACE) 15 | 16 | /// Information record describing a Python buffer object 17 | struct buffer_info { 18 | void *ptr = nullptr; // Pointer to the underlying storage 19 | ssize_t itemsize = 0; // Size of individual items in bytes 20 | ssize_t size = 0; // Total number of entries 21 | std::string format; // For homogeneous buffers, this should be set to format_descriptor::format() 22 | ssize_t ndim = 0; // Number of dimensions 23 | std::vector shape; // Shape of the tensor (1 entry per dimension) 24 | std::vector strides; // Number of entries between adjacent entries (for each per dimension) 25 | 26 | buffer_info() { } 27 | 28 | buffer_info(void *ptr, ssize_t itemsize, const std::string &format, ssize_t ndim, 29 | detail::any_container shape_in, detail::any_container strides_in) 30 | : ptr(ptr), itemsize(itemsize), size(1), format(format), ndim(ndim), 31 | shape(std::move(shape_in)), strides(std::move(strides_in)) { 32 | if (ndim != (ssize_t)shape.size() || ndim != (ssize_t)strides.size()) 33 | pybind11_fail("buffer_info: ndim doesn't match shape and/or strides length"); 34 | for (size_t i = 0; i < (size_t)ndim; ++i) 35 | size *= shape[i]; 36 | } 37 | 38 | template 39 | buffer_info(T *ptr, detail::any_container shape_in, detail::any_container strides_in) 40 | : buffer_info(private_ctr_tag(), ptr, sizeof(T), format_descriptor::format(), static_cast(shape_in->size()), std::move(shape_in), std::move(strides_in)) { } 41 | 42 | buffer_info(void *ptr, ssize_t itemsize, const std::string &format, ssize_t size) 43 | : buffer_info(ptr, itemsize, format, 1, { size }, { itemsize }) { } 44 | 45 | template 46 | buffer_info(T *ptr, ssize_t size) 47 | : buffer_info(ptr, sizeof(T), format_descriptor::format(), size) { } 48 | 49 | explicit buffer_info(Py_buffer *view, bool ownview = true) 50 | : buffer_info(view->buf, view->itemsize, view->format, view->ndim, 51 | { view->shape, view->shape + view->ndim }, { view->strides, view->strides + view->ndim }) { 52 | this->view = view; 53 | this->ownview = ownview; 54 | } 55 | 56 | buffer_info(const buffer_info &) = delete; 57 | buffer_info& operator=(const buffer_info &) = delete; 58 | 59 | buffer_info(buffer_info &&other) { 60 | (*this) = std::move(other); 61 | } 62 | 63 | buffer_info& operator=(buffer_info &&rhs) { 64 | ptr = rhs.ptr; 65 | itemsize = rhs.itemsize; 66 | size = rhs.size; 67 | format = std::move(rhs.format); 68 | ndim = rhs.ndim; 69 | shape = std::move(rhs.shape); 70 | strides = std::move(rhs.strides); 71 | std::swap(view, rhs.view); 72 | std::swap(ownview, rhs.ownview); 73 | return *this; 74 | } 75 | 76 | ~buffer_info() { 77 | if (view && ownview) { PyBuffer_Release(view); delete view; } 78 | } 79 | 80 | private: 81 | struct private_ctr_tag { }; 82 | 83 | buffer_info(private_ctr_tag, void *ptr, ssize_t itemsize, const std::string &format, ssize_t ndim, 84 | detail::any_container &&shape_in, detail::any_container &&strides_in) 85 | : buffer_info(ptr, itemsize, format, ndim, std::move(shape_in), std::move(strides_in)) { } 86 | 87 | Py_buffer *view = nullptr; 88 | bool ownview = false; 89 | }; 90 | 91 | NAMESPACE_BEGIN(detail) 92 | 93 | template struct compare_buffer_info { 94 | static bool compare(const buffer_info& b) { 95 | return b.format == format_descriptor::format() && b.itemsize == (ssize_t) sizeof(T); 96 | } 97 | }; 98 | 99 | template struct compare_buffer_info::value>> { 100 | static bool compare(const buffer_info& b) { 101 | return (size_t)b.itemsize == sizeof(T) && (b.format == format_descriptor::value || 102 | ((sizeof(T) == sizeof(long)) && b.format == (std::is_unsigned::value ? "L" : "l")) || 103 | ((sizeof(T) == sizeof(size_t)) && b.format == (std::is_unsigned::value ? "N" : "n"))); 104 | } 105 | }; 106 | 107 | NAMESPACE_END(detail) 108 | NAMESPACE_END(PYBIND11_NAMESPACE) 109 | -------------------------------------------------------------------------------- /src/include/pybind11/chrono.h: -------------------------------------------------------------------------------- 1 | /* 2 | pybind11/chrono.h: Transparent conversion between std::chrono and python's datetime 3 | 4 | Copyright (c) 2016 Trent Houliston and 5 | Wenzel Jakob 6 | 7 | All rights reserved. Use of this source code is governed by a 8 | BSD-style license that can be found in the LICENSE file. 9 | */ 10 | 11 | #pragma once 12 | 13 | #include "pybind11.h" 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | // Backport the PyDateTime_DELTA functions from Python3.3 if required 20 | #ifndef PyDateTime_DELTA_GET_DAYS 21 | #define PyDateTime_DELTA_GET_DAYS(o) (((PyDateTime_Delta*)o)->days) 22 | #endif 23 | #ifndef PyDateTime_DELTA_GET_SECONDS 24 | #define PyDateTime_DELTA_GET_SECONDS(o) (((PyDateTime_Delta*)o)->seconds) 25 | #endif 26 | #ifndef PyDateTime_DELTA_GET_MICROSECONDS 27 | #define PyDateTime_DELTA_GET_MICROSECONDS(o) (((PyDateTime_Delta*)o)->microseconds) 28 | #endif 29 | 30 | NAMESPACE_BEGIN(PYBIND11_NAMESPACE) 31 | NAMESPACE_BEGIN(detail) 32 | 33 | template class duration_caster { 34 | public: 35 | typedef typename type::rep rep; 36 | typedef typename type::period period; 37 | 38 | typedef std::chrono::duration> days; 39 | 40 | bool load(handle src, bool) { 41 | using namespace std::chrono; 42 | 43 | // Lazy initialise the PyDateTime import 44 | if (!PyDateTimeAPI) { PyDateTime_IMPORT; } 45 | 46 | if (!src) return false; 47 | // If invoked with datetime.delta object 48 | if (PyDelta_Check(src.ptr())) { 49 | value = type(duration_cast>( 50 | days(PyDateTime_DELTA_GET_DAYS(src.ptr())) 51 | + seconds(PyDateTime_DELTA_GET_SECONDS(src.ptr())) 52 | + microseconds(PyDateTime_DELTA_GET_MICROSECONDS(src.ptr())))); 53 | return true; 54 | } 55 | // If invoked with a float we assume it is seconds and convert 56 | else if (PyFloat_Check(src.ptr())) { 57 | value = type(duration_cast>(duration(PyFloat_AsDouble(src.ptr())))); 58 | return true; 59 | } 60 | else return false; 61 | } 62 | 63 | // If this is a duration just return it back 64 | static const std::chrono::duration& get_duration(const std::chrono::duration &src) { 65 | return src; 66 | } 67 | 68 | // If this is a time_point get the time_since_epoch 69 | template static std::chrono::duration get_duration(const std::chrono::time_point> &src) { 70 | return src.time_since_epoch(); 71 | } 72 | 73 | static handle cast(const type &src, return_value_policy /* policy */, handle /* parent */) { 74 | using namespace std::chrono; 75 | 76 | // Use overloaded function to get our duration from our source 77 | // Works out if it is a duration or time_point and get the duration 78 | auto d = get_duration(src); 79 | 80 | // Lazy initialise the PyDateTime import 81 | if (!PyDateTimeAPI) { PyDateTime_IMPORT; } 82 | 83 | // Declare these special duration types so the conversions happen with the correct primitive types (int) 84 | using dd_t = duration>; 85 | using ss_t = duration>; 86 | using us_t = duration; 87 | 88 | auto dd = duration_cast(d); 89 | auto subd = d - dd; 90 | auto ss = duration_cast(subd); 91 | auto us = duration_cast(subd - ss); 92 | return PyDelta_FromDSU(dd.count(), ss.count(), us.count()); 93 | } 94 | 95 | PYBIND11_TYPE_CASTER(type, _("datetime.timedelta")); 96 | }; 97 | 98 | // This is for casting times on the system clock into datetime.datetime instances 99 | template class type_caster> { 100 | public: 101 | typedef std::chrono::time_point type; 102 | bool load(handle src, bool) { 103 | using namespace std::chrono; 104 | 105 | // Lazy initialise the PyDateTime import 106 | if (!PyDateTimeAPI) { PyDateTime_IMPORT; } 107 | 108 | if (!src) return false; 109 | if (PyDateTime_Check(src.ptr())) { 110 | std::tm cal; 111 | cal.tm_sec = PyDateTime_DATE_GET_SECOND(src.ptr()); 112 | cal.tm_min = PyDateTime_DATE_GET_MINUTE(src.ptr()); 113 | cal.tm_hour = PyDateTime_DATE_GET_HOUR(src.ptr()); 114 | cal.tm_mday = PyDateTime_GET_DAY(src.ptr()); 115 | cal.tm_mon = PyDateTime_GET_MONTH(src.ptr()) - 1; 116 | cal.tm_year = PyDateTime_GET_YEAR(src.ptr()) - 1900; 117 | cal.tm_isdst = -1; 118 | 119 | value = system_clock::from_time_t(std::mktime(&cal)) + microseconds(PyDateTime_DATE_GET_MICROSECOND(src.ptr())); 120 | return true; 121 | } 122 | else return false; 123 | } 124 | 125 | static handle cast(const std::chrono::time_point &src, return_value_policy /* policy */, handle /* parent */) { 126 | using namespace std::chrono; 127 | 128 | // Lazy initialise the PyDateTime import 129 | if (!PyDateTimeAPI) { PyDateTime_IMPORT; } 130 | 131 | std::time_t tt = system_clock::to_time_t(src); 132 | // this function uses static memory so it's best to copy it out asap just in case 133 | // otherwise other code that is using localtime may break this (not just python code) 134 | std::tm localtime = *std::localtime(&tt); 135 | 136 | // Declare these special duration types so the conversions happen with the correct primitive types (int) 137 | using us_t = duration; 138 | 139 | return PyDateTime_FromDateAndTime(localtime.tm_year + 1900, 140 | localtime.tm_mon + 1, 141 | localtime.tm_mday, 142 | localtime.tm_hour, 143 | localtime.tm_min, 144 | localtime.tm_sec, 145 | (duration_cast(src.time_since_epoch() % seconds(1))).count()); 146 | } 147 | PYBIND11_TYPE_CASTER(type, _("datetime.datetime")); 148 | }; 149 | 150 | // Other clocks that are not the system clock are not measured as datetime.datetime objects 151 | // since they are not measured on calendar time. So instead we just make them timedeltas 152 | // Or if they have passed us a time as a float we convert that 153 | template class type_caster> 154 | : public duration_caster> { 155 | }; 156 | 157 | template class type_caster> 158 | : public duration_caster> { 159 | }; 160 | 161 | NAMESPACE_END(detail) 162 | NAMESPACE_END(PYBIND11_NAMESPACE) 163 | -------------------------------------------------------------------------------- /src/include/pybind11/common.h: -------------------------------------------------------------------------------- 1 | #include "detail/common.h" 2 | #warning "Including 'common.h' is deprecated. It will be removed in v3.0. Use 'pybind11.h'." 3 | -------------------------------------------------------------------------------- /src/include/pybind11/complex.h: -------------------------------------------------------------------------------- 1 | /* 2 | pybind11/complex.h: Complex number support 3 | 4 | Copyright (c) 2016 Wenzel Jakob 5 | 6 | All rights reserved. Use of this source code is governed by a 7 | BSD-style license that can be found in the LICENSE file. 8 | */ 9 | 10 | #pragma once 11 | 12 | #include "pybind11.h" 13 | #include 14 | 15 | /// glibc defines I as a macro which breaks things, e.g., boost template names 16 | #ifdef I 17 | # undef I 18 | #endif 19 | 20 | NAMESPACE_BEGIN(PYBIND11_NAMESPACE) 21 | 22 | template struct format_descriptor, detail::enable_if_t::value>> { 23 | static constexpr const char c = format_descriptor::c; 24 | static constexpr const char value[3] = { 'Z', c, '\0' }; 25 | static std::string format() { return std::string(value); } 26 | }; 27 | 28 | #ifndef PYBIND11_CPP17 29 | 30 | template constexpr const char format_descriptor< 31 | std::complex, detail::enable_if_t::value>>::value[3]; 32 | 33 | #endif 34 | 35 | NAMESPACE_BEGIN(detail) 36 | 37 | template struct is_fmt_numeric, detail::enable_if_t::value>> { 38 | static constexpr bool value = true; 39 | static constexpr int index = is_fmt_numeric::index + 3; 40 | }; 41 | 42 | template class type_caster> { 43 | public: 44 | bool load(handle src, bool convert) { 45 | if (!src) 46 | return false; 47 | if (!convert && !PyComplex_Check(src.ptr())) 48 | return false; 49 | Py_complex result = PyComplex_AsCComplex(src.ptr()); 50 | if (result.real == -1.0 && PyErr_Occurred()) { 51 | PyErr_Clear(); 52 | return false; 53 | } 54 | value = std::complex((T)result.real, (T)result.imag); 55 | return true; 56 | } 57 | 58 | static handle cast(const std::complex &src, return_value_policy /* policy */, handle /* parent */) { 59 | return PyComplex_FromDoubles((double)src.real(), (double)src.imag()); 60 | } 61 | 62 | PYBIND11_TYPE_CASTER(std::complex, _("complex")); 63 | }; 64 | NAMESPACE_END(detail) 65 | NAMESPACE_END(PYBIND11_NAMESPACE) 66 | -------------------------------------------------------------------------------- /src/include/pybind11/detail/descr.h: -------------------------------------------------------------------------------- 1 | /* 2 | pybind11/detail/descr.h: Helper type for concatenating type signatures at compile time 3 | 4 | Copyright (c) 2016 Wenzel Jakob 5 | 6 | All rights reserved. Use of this source code is governed by a 7 | BSD-style license that can be found in the LICENSE file. 8 | */ 9 | 10 | #pragma once 11 | 12 | #include "common.h" 13 | 14 | NAMESPACE_BEGIN(PYBIND11_NAMESPACE) 15 | NAMESPACE_BEGIN(detail) 16 | 17 | #if !defined(_MSC_VER) 18 | # define PYBIND11_DESCR_CONSTEXPR static constexpr 19 | #else 20 | # define PYBIND11_DESCR_CONSTEXPR const 21 | #endif 22 | 23 | /* Concatenate type signatures at compile time */ 24 | template 25 | struct descr { 26 | char text[N + 1]; 27 | 28 | constexpr descr() : text{ '\0' } { } 29 | constexpr descr(char const (&s)[N + 1]) : descr(s, make_index_sequence()) { } 30 | 31 | template 32 | constexpr descr(char const (&s)[N + 1], index_sequence) : text{ s[Is]..., '\0' } { } 33 | 34 | template 35 | constexpr descr(char c, Chars... cs) : text{ c, static_cast(cs)..., '\0' } { } 36 | 37 | static constexpr std::array types() { 38 | return { {&typeid(Ts)..., nullptr} }; 39 | } 40 | }; 41 | 42 | template 43 | constexpr descr plus_impl(const descr &a, const descr &b, 44 | index_sequence, index_sequence) { 45 | return { a.text[Is1]..., b.text[Is2]... }; 46 | } 47 | 48 | template 49 | constexpr descr operator+(const descr &a, const descr &b) { 50 | return plus_impl(a, b, make_index_sequence(), make_index_sequence()); 51 | } 52 | 53 | template 54 | constexpr descr _(char const(&text)[N]) { return descr(text); } 55 | constexpr descr<0> _(char const(&)[1]) { return {}; } 56 | 57 | template struct int_to_str : int_to_str { }; 58 | template struct int_to_str<0, Digits...> { 59 | static constexpr auto digits = descr(('0' + Digits)...); 60 | }; 61 | 62 | // Ternary description (like std::conditional) 63 | template 64 | constexpr enable_if_t> _(char const(&text1)[N1], char const(&)[N2]) { 65 | return _(text1); 66 | } 67 | template 68 | constexpr enable_if_t> _(char const(&)[N1], char const(&text2)[N2]) { 69 | return _(text2); 70 | } 71 | 72 | template 73 | constexpr enable_if_t _(const T1 &d, const T2 &) { return d; } 74 | template 75 | constexpr enable_if_t _(const T1 &, const T2 &d) { return d; } 76 | 77 | template auto constexpr _() -> decltype(int_to_str::digits) { 78 | return int_to_str::digits; 79 | } 80 | 81 | template constexpr descr<1, Type> _() { return { '%' }; } 82 | 83 | constexpr descr<0> concat() { return {}; } 84 | 85 | template 86 | constexpr descr concat(const descr &descr) { return descr; } 87 | 88 | template 89 | constexpr auto concat(const descr &d, const Args &...args) 90 | -> decltype(std::declval>() + concat(args...)) { 91 | return d + _(", ") + concat(args...); 92 | } 93 | 94 | template 95 | constexpr descr type_descr(const descr &descr) { 96 | return _("{") + descr + _("}"); 97 | } 98 | 99 | NAMESPACE_END(detail) 100 | NAMESPACE_END(PYBIND11_NAMESPACE) 101 | -------------------------------------------------------------------------------- /src/include/pybind11/detail/typeid.h: -------------------------------------------------------------------------------- 1 | /* 2 | pybind11/detail/typeid.h: Compiler-independent access to type identifiers 3 | 4 | Copyright (c) 2016 Wenzel Jakob 5 | 6 | All rights reserved. Use of this source code is governed by a 7 | BSD-style license that can be found in the LICENSE file. 8 | */ 9 | 10 | #pragma once 11 | 12 | #include 13 | #include 14 | 15 | #if defined(__GNUG__) 16 | #include 17 | #endif 18 | 19 | NAMESPACE_BEGIN(PYBIND11_NAMESPACE) 20 | NAMESPACE_BEGIN(detail) 21 | /// Erase all occurrences of a substring 22 | inline void erase_all(std::string &string, const std::string &search) { 23 | for (size_t pos = 0;;) { 24 | pos = string.find(search, pos); 25 | if (pos == std::string::npos) break; 26 | string.erase(pos, search.length()); 27 | } 28 | } 29 | 30 | PYBIND11_NOINLINE inline void clean_type_id(std::string &name) { 31 | #if defined(__GNUG__) 32 | int status = 0; 33 | std::unique_ptr res{ 34 | abi::__cxa_demangle(name.c_str(), nullptr, nullptr, &status), std::free }; 35 | if (status == 0) 36 | name = res.get(); 37 | #else 38 | detail::erase_all(name, "class "); 39 | detail::erase_all(name, "struct "); 40 | detail::erase_all(name, "enum "); 41 | #endif 42 | detail::erase_all(name, "pybind11::"); 43 | } 44 | NAMESPACE_END(detail) 45 | 46 | /// Return a string representation of a C++ type 47 | template static std::string type_id() { 48 | std::string name(typeid(T).name()); 49 | detail::clean_type_id(name); 50 | return name; 51 | } 52 | 53 | NAMESPACE_END(PYBIND11_NAMESPACE) 54 | -------------------------------------------------------------------------------- /src/include/pybind11/embed.h: -------------------------------------------------------------------------------- 1 | /* 2 | pybind11/embed.h: Support for embedding the interpreter 3 | 4 | Copyright (c) 2017 Wenzel Jakob 5 | 6 | All rights reserved. Use of this source code is governed by a 7 | BSD-style license that can be found in the LICENSE file. 8 | */ 9 | 10 | #pragma once 11 | 12 | #include "pybind11.h" 13 | #include "eval.h" 14 | 15 | #if defined(PYPY_VERSION) 16 | # error Embedding the interpreter is not supported with PyPy 17 | #endif 18 | 19 | #if PY_MAJOR_VERSION >= 3 20 | # define PYBIND11_EMBEDDED_MODULE_IMPL(name) \ 21 | extern "C" PyObject *pybind11_init_impl_##name() { \ 22 | return pybind11_init_wrapper_##name(); \ 23 | } 24 | #else 25 | # define PYBIND11_EMBEDDED_MODULE_IMPL(name) \ 26 | extern "C" void pybind11_init_impl_##name() { \ 27 | pybind11_init_wrapper_##name(); \ 28 | } 29 | #endif 30 | 31 | /** \rst 32 | Add a new module to the table of builtins for the interpreter. Must be 33 | defined in global scope. The first macro parameter is the name of the 34 | module (without quotes). The second parameter is the variable which will 35 | be used as the interface to add functions and classes to the module. 36 | 37 | .. code-block:: cpp 38 | 39 | PYBIND11_EMBEDDED_MODULE(example, m) { 40 | // ... initialize functions and classes here 41 | m.def("foo", []() { 42 | return "Hello, World!"; 43 | }); 44 | } 45 | \endrst */ 46 | #define PYBIND11_EMBEDDED_MODULE(name, variable) \ 47 | static void PYBIND11_CONCAT(pybind11_init_, name)(pybind11::module &); \ 48 | static PyObject PYBIND11_CONCAT(*pybind11_init_wrapper_, name)() { \ 49 | auto m = pybind11::module(PYBIND11_TOSTRING(name)); \ 50 | try { \ 51 | PYBIND11_CONCAT(pybind11_init_, name)(m); \ 52 | return m.ptr(); \ 53 | } catch (pybind11::error_already_set &e) { \ 54 | PyErr_SetString(PyExc_ImportError, e.what()); \ 55 | return nullptr; \ 56 | } catch (const std::exception &e) { \ 57 | PyErr_SetString(PyExc_ImportError, e.what()); \ 58 | return nullptr; \ 59 | } \ 60 | } \ 61 | PYBIND11_EMBEDDED_MODULE_IMPL(name) \ 62 | pybind11::detail::embedded_module name(PYBIND11_TOSTRING(name), \ 63 | PYBIND11_CONCAT(pybind11_init_impl_, name)); \ 64 | void PYBIND11_CONCAT(pybind11_init_, name)(pybind11::module &variable) 65 | 66 | NAMESPACE_BEGIN(PYBIND11_NAMESPACE) 67 | NAMESPACE_BEGIN(detail) 68 | 69 | /// Python 2.7/3.x compatible version of `PyImport_AppendInittab` and error checks. 70 | struct embedded_module { 71 | #if PY_MAJOR_VERSION >= 3 72 | using init_t = PyObject * (*)(); 73 | #else 74 | using init_t = void(*)(); 75 | #endif 76 | embedded_module(const char *name, init_t init) { 77 | if (Py_IsInitialized()) 78 | pybind11_fail("Can't add new modules after the interpreter has been initialized"); 79 | 80 | auto result = PyImport_AppendInittab(name, init); 81 | if (result == -1) 82 | pybind11_fail("Insufficient memory to add a new module"); 83 | } 84 | }; 85 | 86 | NAMESPACE_END(detail) 87 | 88 | /** \rst 89 | Initialize the Python interpreter. No other pybind11 or CPython API functions can be 90 | called before this is done; with the exception of `PYBIND11_EMBEDDED_MODULE`. The 91 | optional parameter can be used to skip the registration of signal handlers (see the 92 | `Python documentation`_ for details). Calling this function again after the interpreter 93 | has already been initialized is a fatal error. 94 | 95 | If initializing the Python interpreter fails, then the program is terminated. (This 96 | is controlled by the CPython runtime and is an exception to pybind11's normal behavior 97 | of throwing exceptions on errors.) 98 | 99 | .. _Python documentation: https://docs.python.org/3/c-api/init.html#c.Py_InitializeEx 100 | \endrst */ 101 | inline void initialize_interpreter(bool init_signal_handlers = true) { 102 | if (Py_IsInitialized()) 103 | pybind11_fail("The interpreter is already running"); 104 | 105 | Py_InitializeEx(init_signal_handlers ? 1 : 0); 106 | 107 | // Make .py files in the working directory available by default 108 | module::import("sys").attr("path").cast().append("."); 109 | } 110 | 111 | /** \rst 112 | Shut down the Python interpreter. No pybind11 or CPython API functions can be called 113 | after this. In addition, pybind11 objects must not outlive the interpreter: 114 | 115 | .. code-block:: cpp 116 | 117 | { // BAD 118 | py::initialize_interpreter(); 119 | auto hello = py::str("Hello, World!"); 120 | py::finalize_interpreter(); 121 | } // <-- BOOM, hello's destructor is called after interpreter shutdown 122 | 123 | { // GOOD 124 | py::initialize_interpreter(); 125 | { // scoped 126 | auto hello = py::str("Hello, World!"); 127 | } // <-- OK, hello is cleaned up properly 128 | py::finalize_interpreter(); 129 | } 130 | 131 | { // BETTER 132 | py::scoped_interpreter guard{}; 133 | auto hello = py::str("Hello, World!"); 134 | } 135 | 136 | .. warning:: 137 | 138 | The interpreter can be restarted by calling `initialize_interpreter` again. 139 | Modules created using pybind11 can be safely re-initialized. However, Python 140 | itself cannot completely unload binary extension modules and there are several 141 | caveats with regard to interpreter restarting. All the details can be found 142 | in the CPython documentation. In short, not all interpreter memory may be 143 | freed, either due to reference cycles or user-created global data. 144 | 145 | \endrst */ 146 | inline void finalize_interpreter() { 147 | handle builtins(PyEval_GetBuiltins()); 148 | const char *id = PYBIND11_INTERNALS_ID; 149 | 150 | // Get the internals pointer (without creating it if it doesn't exist). It's possible for the 151 | // internals to be created during Py_Finalize() (e.g. if a py::capsule calls `get_internals()` 152 | // during destruction), so we get the pointer-pointer here and check it after Py_Finalize(). 153 | detail::internals **internals_ptr_ptr = detail::get_internals_pp(); 154 | // It could also be stashed in builtins, so look there too: 155 | if (builtins.contains(id) && isinstance(builtins[id])) 156 | internals_ptr_ptr = capsule(builtins[id]); 157 | 158 | Py_Finalize(); 159 | 160 | if (internals_ptr_ptr) { 161 | delete *internals_ptr_ptr; 162 | *internals_ptr_ptr = nullptr; 163 | } 164 | } 165 | 166 | /** \rst 167 | Scope guard version of `initialize_interpreter` and `finalize_interpreter`. 168 | This a move-only guard and only a single instance can exist. 169 | 170 | .. code-block:: cpp 171 | 172 | #include 173 | 174 | int main() { 175 | py::scoped_interpreter guard{}; 176 | py::print(Hello, World!); 177 | } // <-- interpreter shutdown 178 | \endrst */ 179 | class scoped_interpreter { 180 | public: 181 | scoped_interpreter(bool init_signal_handlers = true) { 182 | initialize_interpreter(init_signal_handlers); 183 | } 184 | 185 | scoped_interpreter(const scoped_interpreter &) = delete; 186 | scoped_interpreter(scoped_interpreter &&other) noexcept { other.is_valid = false; } 187 | scoped_interpreter &operator=(const scoped_interpreter &) = delete; 188 | scoped_interpreter &operator=(scoped_interpreter &&) = delete; 189 | 190 | ~scoped_interpreter() { 191 | if (is_valid) 192 | finalize_interpreter(); 193 | } 194 | 195 | private: 196 | bool is_valid = true; 197 | }; 198 | 199 | NAMESPACE_END(PYBIND11_NAMESPACE) 200 | -------------------------------------------------------------------------------- /src/include/pybind11/eval.h: -------------------------------------------------------------------------------- 1 | /* 2 | pybind11/exec.h: Support for evaluating Python expressions and statements 3 | from strings and files 4 | 5 | Copyright (c) 2016 Klemens Morgenstern and 6 | Wenzel Jakob 7 | 8 | All rights reserved. Use of this source code is governed by a 9 | BSD-style license that can be found in the LICENSE file. 10 | */ 11 | 12 | #pragma once 13 | 14 | #include "pybind11.h" 15 | 16 | NAMESPACE_BEGIN(PYBIND11_NAMESPACE) 17 | 18 | enum eval_mode { 19 | /// Evaluate a string containing an isolated expression 20 | eval_expr, 21 | 22 | /// Evaluate a string containing a single statement. Returns \c none 23 | eval_single_statement, 24 | 25 | /// Evaluate a string containing a sequence of statement. Returns \c none 26 | eval_statements 27 | }; 28 | 29 | template 30 | object eval(str expr, object global = globals(), object local = object()) { 31 | if (!local) 32 | local = global; 33 | 34 | /* PyRun_String does not accept a PyObject / encoding specifier, 35 | this seems to be the only alternative */ 36 | std::string buffer = "# -*- coding: utf-8 -*-\n" + (std::string) expr; 37 | 38 | int start; 39 | switch (mode) { 40 | case eval_expr: start = Py_eval_input; break; 41 | case eval_single_statement: start = Py_single_input; break; 42 | case eval_statements: start = Py_file_input; break; 43 | default: pybind11_fail("invalid evaluation mode"); 44 | } 45 | 46 | PyObject *result = PyRun_String(buffer.c_str(), start, global.ptr(), local.ptr()); 47 | if (!result) 48 | throw error_already_set(); 49 | return reinterpret_steal(result); 50 | } 51 | 52 | template 53 | object eval(const char(&s)[N], object global = globals(), object local = object()) { 54 | /* Support raw string literals by removing common leading whitespace */ 55 | auto expr = (s[0] == '\n') ? str(module::import("textwrap").attr("dedent")(s)) 56 | : str(s); 57 | return eval(expr, global, local); 58 | } 59 | 60 | inline void exec(str expr, object global = globals(), object local = object()) { 61 | eval(expr, global, local); 62 | } 63 | 64 | template 65 | void exec(const char(&s)[N], object global = globals(), object local = object()) { 66 | eval(s, global, local); 67 | } 68 | 69 | template 70 | object eval_file(str fname, object global = globals(), object local = object()) { 71 | if (!local) 72 | local = global; 73 | 74 | int start; 75 | switch (mode) { 76 | case eval_expr: start = Py_eval_input; break; 77 | case eval_single_statement: start = Py_single_input; break; 78 | case eval_statements: start = Py_file_input; break; 79 | default: pybind11_fail("invalid evaluation mode"); 80 | } 81 | 82 | int closeFile = 1; 83 | std::string fname_str = (std::string) fname; 84 | #if PY_VERSION_HEX >= 0x03040000 85 | FILE *f = _Py_fopen_obj(fname.ptr(), "r"); 86 | #elif PY_VERSION_HEX >= 0x03000000 87 | FILE *f = _Py_fopen(fname.ptr(), "r"); 88 | #else 89 | /* No unicode support in open() :( */ 90 | auto fobj = reinterpret_steal(PyFile_FromString( 91 | const_cast(fname_str.c_str()), 92 | const_cast("r"))); 93 | FILE *f = nullptr; 94 | if (fobj) 95 | f = PyFile_AsFile(fobj.ptr()); 96 | closeFile = 0; 97 | #endif 98 | if (!f) { 99 | PyErr_Clear(); 100 | pybind11_fail("File \"" + fname_str + "\" could not be opened!"); 101 | } 102 | 103 | #if PY_VERSION_HEX < 0x03000000 && defined(PYPY_VERSION) 104 | PyObject *result = PyRun_File(f, fname_str.c_str(), start, global.ptr(), 105 | local.ptr()); 106 | (void)closeFile; 107 | #else 108 | PyObject *result = PyRun_FileEx(f, fname_str.c_str(), start, global.ptr(), 109 | local.ptr(), closeFile); 110 | #endif 111 | 112 | if (!result) 113 | throw error_already_set(); 114 | return reinterpret_steal(result); 115 | } 116 | 117 | NAMESPACE_END(PYBIND11_NAMESPACE) 118 | -------------------------------------------------------------------------------- /src/include/pybind11/functional.h: -------------------------------------------------------------------------------- 1 | /* 2 | pybind11/functional.h: std::function<> support 3 | 4 | Copyright (c) 2016 Wenzel Jakob 5 | 6 | All rights reserved. Use of this source code is governed by a 7 | BSD-style license that can be found in the LICENSE file. 8 | */ 9 | 10 | #pragma once 11 | 12 | #include "pybind11.h" 13 | #include 14 | 15 | NAMESPACE_BEGIN(PYBIND11_NAMESPACE) 16 | NAMESPACE_BEGIN(detail) 17 | 18 | template 19 | struct type_caster> { 20 | using type = std::function; 21 | using retval_type = conditional_t::value, void_type, Return>; 22 | using function_type = Return(*) (Args...); 23 | 24 | public: 25 | bool load(handle src, bool convert) { 26 | if (src.is_none()) { 27 | // Defer accepting None to other overloads (if we aren't in convert mode): 28 | if (!convert) return false; 29 | return true; 30 | } 31 | 32 | if (!isinstance(src)) 33 | return false; 34 | 35 | auto func = reinterpret_borrow(src); 36 | 37 | /* 38 | When passing a C++ function as an argument to another C++ 39 | function via Python, every function call would normally involve 40 | a full C++ -> Python -> C++ roundtrip, which can be prohibitive. 41 | Here, we try to at least detect the case where the function is 42 | stateless (i.e. function pointer or lambda function without 43 | captured variables), in which case the roundtrip can be avoided. 44 | */ 45 | if (auto cfunc = func.cpp_function()) { 46 | auto c = reinterpret_borrow(PyCFunction_GET_SELF(cfunc.ptr())); 47 | auto rec = (function_record *)c; 48 | 49 | if (rec && rec->is_stateless && 50 | same_type(typeid(function_type), *reinterpret_cast(rec->data[1]))) { 51 | struct capture { function_type f; }; 52 | value = ((capture *)&rec->data)->f; 53 | return true; 54 | } 55 | } 56 | 57 | value = [func](Args... args) -> Return { 58 | gil_scoped_acquire acq; 59 | object retval(func(std::forward(args)...)); 60 | /* Visual studio 2015 parser issue: need parentheses around this expression */ 61 | return (retval.template cast()); 62 | }; 63 | return true; 64 | } 65 | 66 | template 67 | static handle cast(Func &&f_, return_value_policy policy, handle /* parent */) { 68 | if (!f_) 69 | return none().inc_ref(); 70 | 71 | auto result = f_.template target(); 72 | if (result) 73 | return cpp_function(*result, policy).release(); 74 | else 75 | return cpp_function(std::forward(f_), policy).release(); 76 | } 77 | 78 | PYBIND11_TYPE_CASTER(type, _("Callable[[") + concat(make_caster::name...) + _("], ") 79 | + make_caster::name + _("]")); 80 | }; 81 | 82 | NAMESPACE_END(detail) 83 | NAMESPACE_END(PYBIND11_NAMESPACE) 84 | -------------------------------------------------------------------------------- /src/include/pybind11/iostream.h: -------------------------------------------------------------------------------- 1 | /* 2 | pybind11/iostream.h -- Tools to assist with redirecting cout and cerr to Python 3 | 4 | Copyright (c) 2017 Henry F. Schreiner 5 | 6 | All rights reserved. Use of this source code is governed by a 7 | BSD-style license that can be found in the LICENSE file. 8 | */ 9 | 10 | #pragma once 11 | 12 | #include "pybind11.h" 13 | 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | NAMESPACE_BEGIN(PYBIND11_NAMESPACE) 21 | NAMESPACE_BEGIN(detail) 22 | 23 | // Buffer that writes to Python instead of C++ 24 | class pythonbuf : public std::streambuf { 25 | private: 26 | using traits_type = std::streambuf::traits_type; 27 | 28 | char d_buffer[1024]; 29 | object pywrite; 30 | object pyflush; 31 | 32 | int overflow(int c) { 33 | if (!traits_type::eq_int_type(c, traits_type::eof())) { 34 | *pptr() = traits_type::to_char_type(c); 35 | pbump(1); 36 | } 37 | return sync() == 0 ? traits_type::not_eof(c) : traits_type::eof(); 38 | } 39 | 40 | int sync() { 41 | if (pbase() != pptr()) { 42 | // This subtraction cannot be negative, so dropping the sign 43 | str line(pbase(), static_cast(pptr() - pbase())); 44 | 45 | pywrite(line); 46 | pyflush(); 47 | 48 | setp(pbase(), epptr()); 49 | } 50 | return 0; 51 | } 52 | 53 | public: 54 | pythonbuf(object pyostream) 55 | : pywrite(pyostream.attr("write")), 56 | pyflush(pyostream.attr("flush")) { 57 | setp(d_buffer, d_buffer + sizeof(d_buffer) - 1); 58 | } 59 | 60 | /// Sync before destroy 61 | ~pythonbuf() { 62 | sync(); 63 | } 64 | }; 65 | 66 | NAMESPACE_END(detail) 67 | 68 | /** \rst 69 | This a move-only guard that redirects output. 70 | 71 | .. code-block:: cpp 72 | 73 | #include 74 | 75 | ... 76 | 77 | { 78 | py::scoped_ostream_redirect output; 79 | std::cout << "Hello, World!"; // Python stdout 80 | } // <-- return std::cout to normal 81 | 82 | You can explicitly pass the c++ stream and the python object, 83 | for example to guard stderr instead. 84 | 85 | .. code-block:: cpp 86 | 87 | { 88 | py::scoped_ostream_redirect output{std::cerr, py::module::import("sys").attr("stderr")}; 89 | std::cerr << "Hello, World!"; 90 | } 91 | \endrst */ 92 | class scoped_ostream_redirect { 93 | protected: 94 | std::streambuf *old; 95 | std::ostream &costream; 96 | detail::pythonbuf buffer; 97 | 98 | public: 99 | scoped_ostream_redirect( 100 | std::ostream &costream = std::cout, 101 | object pyostream = module::import("sys").attr("stdout")) 102 | : costream(costream), buffer(pyostream) { 103 | old = costream.rdbuf(&buffer); 104 | } 105 | 106 | ~scoped_ostream_redirect() { 107 | costream.rdbuf(old); 108 | } 109 | 110 | scoped_ostream_redirect(const scoped_ostream_redirect &) = delete; 111 | scoped_ostream_redirect(scoped_ostream_redirect &&other) = default; 112 | scoped_ostream_redirect &operator=(const scoped_ostream_redirect &) = delete; 113 | scoped_ostream_redirect &operator=(scoped_ostream_redirect &&) = delete; 114 | }; 115 | 116 | /** \rst 117 | Like `scoped_ostream_redirect`, but redirects cerr by default. This class 118 | is provided primary to make ``py::call_guard`` easier to make. 119 | 120 | .. code-block:: cpp 121 | 122 | m.def("noisy_func", &noisy_func, 123 | py::call_guard()); 125 | 126 | \endrst */ 127 | class scoped_estream_redirect : public scoped_ostream_redirect { 128 | public: 129 | scoped_estream_redirect( 130 | std::ostream &costream = std::cerr, 131 | object pyostream = module::import("sys").attr("stderr")) 132 | : scoped_ostream_redirect(costream, pyostream) {} 133 | }; 134 | 135 | NAMESPACE_BEGIN(detail) 136 | 137 | // Class to redirect output as a context manager. C++ backend. 138 | class OstreamRedirect { 139 | bool do_stdout_; 140 | bool do_stderr_; 141 | std::unique_ptr redirect_stdout; 142 | std::unique_ptr redirect_stderr; 143 | 144 | public: 145 | OstreamRedirect(bool do_stdout = true, bool do_stderr = true) 146 | : do_stdout_(do_stdout), do_stderr_(do_stderr) {} 147 | 148 | void enter() { 149 | if (do_stdout_) 150 | redirect_stdout.reset(new scoped_ostream_redirect()); 151 | if (do_stderr_) 152 | redirect_stderr.reset(new scoped_estream_redirect()); 153 | } 154 | 155 | void exit() { 156 | redirect_stdout.reset(); 157 | redirect_stderr.reset(); 158 | } 159 | }; 160 | 161 | NAMESPACE_END(detail) 162 | 163 | /** \rst 164 | This is a helper function to add a C++ redirect context manager to Python 165 | instead of using a C++ guard. To use it, add the following to your binding code: 166 | 167 | .. code-block:: cpp 168 | 169 | #include 170 | 171 | ... 172 | 173 | py::add_ostream_redirect(m, "ostream_redirect"); 174 | 175 | You now have a Python context manager that redirects your output: 176 | 177 | .. code-block:: python 178 | 179 | with m.ostream_redirect(): 180 | m.print_to_cout_function() 181 | 182 | This manager can optionally be told which streams to operate on: 183 | 184 | .. code-block:: python 185 | 186 | with m.ostream_redirect(stdout=true, stderr=true): 187 | m.noisy_function_with_error_printing() 188 | 189 | \endrst */ 190 | inline class_ add_ostream_redirect(module m, std::string name = "ostream_redirect") { 191 | return class_(m, name.c_str(), module_local()) 192 | .def(init(), arg("stdout") = true, arg("stderr") = true) 193 | .def("__enter__", &detail::OstreamRedirect::enter) 194 | .def("__exit__", [](detail::OstreamRedirect &self_, args) { self_.exit(); }); 195 | } 196 | 197 | NAMESPACE_END(PYBIND11_NAMESPACE) 198 | -------------------------------------------------------------------------------- /src/include/pybind11/operators.h: -------------------------------------------------------------------------------- 1 | /* 2 | pybind11/operator.h: Metatemplates for operator overloading 3 | 4 | Copyright (c) 2016 Wenzel Jakob 5 | 6 | All rights reserved. Use of this source code is governed by a 7 | BSD-style license that can be found in the LICENSE file. 8 | */ 9 | 10 | #pragma once 11 | 12 | #include "pybind11.h" 13 | 14 | #if defined(__clang__) && !defined(__INTEL_COMPILER) 15 | # pragma clang diagnostic ignored "-Wunsequenced" // multiple unsequenced modifications to 'self' (when using def(py::self OP Type())) 16 | #elif defined(_MSC_VER) 17 | # pragma warning(push) 18 | # pragma warning(disable: 4127) // warning C4127: Conditional expression is constant 19 | #endif 20 | 21 | NAMESPACE_BEGIN(PYBIND11_NAMESPACE) 22 | NAMESPACE_BEGIN(detail) 23 | 24 | /// Enumeration with all supported operator types 25 | enum op_id : int { 26 | op_add, op_sub, op_mul, op_div, op_mod, op_divmod, op_pow, op_lshift, 27 | op_rshift, op_and, op_xor, op_or, op_neg, op_pos, op_abs, op_invert, 28 | op_int, op_long, op_float, op_str, op_cmp, op_gt, op_ge, op_lt, op_le, 29 | op_eq, op_ne, op_iadd, op_isub, op_imul, op_idiv, op_imod, op_ilshift, 30 | op_irshift, op_iand, op_ixor, op_ior, op_complex, op_bool, op_nonzero, 31 | op_repr, op_truediv, op_itruediv, op_hash 32 | }; 33 | 34 | enum op_type : int { 35 | op_l, /* base type on left */ 36 | op_r, /* base type on right */ 37 | op_u /* unary operator */ 38 | }; 39 | 40 | struct self_t { }; 41 | static const self_t self = self_t(); 42 | 43 | /// Type for an unused type slot 44 | struct undefined_t { }; 45 | 46 | /// Don't warn about an unused variable 47 | inline self_t __self() { return self; } 48 | 49 | /// base template of operator implementations 50 | template struct op_impl { }; 51 | 52 | /// Operator implementation generator 53 | template struct op_ { 54 | template void execute(Class &cl, const Extra&... extra) const { 55 | using Base = typename Class::type; 56 | using L_type = conditional_t::value, Base, L>; 57 | using R_type = conditional_t::value, Base, R>; 58 | using op = op_impl; 59 | cl.def(op::name(), &op::execute, is_operator(), extra...); 60 | #if PY_MAJOR_VERSION < 3 61 | if (id == op_truediv || id == op_itruediv) 62 | cl.def(id == op_itruediv ? "__idiv__" : ot == op_l ? "__div__" : "__rdiv__", 63 | &op::execute, is_operator(), extra...); 64 | #endif 65 | } 66 | template void execute_cast(Class &cl, const Extra&... extra) const { 67 | using Base = typename Class::type; 68 | using L_type = conditional_t::value, Base, L>; 69 | using R_type = conditional_t::value, Base, R>; 70 | using op = op_impl; 71 | cl.def(op::name(), &op::execute_cast, is_operator(), extra...); 72 | #if PY_MAJOR_VERSION < 3 73 | if (id == op_truediv || id == op_itruediv) 74 | cl.def(id == op_itruediv ? "__idiv__" : ot == op_l ? "__div__" : "__rdiv__", 75 | &op::execute, is_operator(), extra...); 76 | #endif 77 | } 78 | }; 79 | 80 | #define PYBIND11_BINARY_OPERATOR(id, rid, op, expr) \ 81 | template struct op_impl { \ 82 | static char const* name() { return "__" #id "__"; } \ 83 | static auto execute(const L &l, const R &r) -> decltype(expr) { return (expr); } \ 84 | static B execute_cast(const L &l, const R &r) { return B(expr); } \ 85 | }; \ 86 | template struct op_impl { \ 87 | static char const* name() { return "__" #rid "__"; } \ 88 | static auto execute(const R &r, const L &l) -> decltype(expr) { return (expr); } \ 89 | static B execute_cast(const R &r, const L &l) { return B(expr); } \ 90 | }; \ 91 | inline op_ op(const self_t &, const self_t &) { \ 92 | return op_(); \ 93 | } \ 94 | template op_ op(const self_t &, const T &) { \ 95 | return op_(); \ 96 | } \ 97 | template op_ op(const T &, const self_t &) { \ 98 | return op_(); \ 99 | } 100 | 101 | #define PYBIND11_INPLACE_OPERATOR(id, op, expr) \ 102 | template struct op_impl { \ 103 | static char const* name() { return "__" #id "__"; } \ 104 | static auto execute(L &l, const R &r) -> decltype(expr) { return expr; } \ 105 | static B execute_cast(L &l, const R &r) { return B(expr); } \ 106 | }; \ 107 | template op_ op(const self_t &, const T &) { \ 108 | return op_(); \ 109 | } 110 | 111 | #define PYBIND11_UNARY_OPERATOR(id, op, expr) \ 112 | template struct op_impl { \ 113 | static char const* name() { return "__" #id "__"; } \ 114 | static auto execute(const L &l) -> decltype(expr) { return expr; } \ 115 | static B execute_cast(const L &l) { return B(expr); } \ 116 | }; \ 117 | inline op_ op(const self_t &) { \ 118 | return op_(); \ 119 | } 120 | 121 | PYBIND11_BINARY_OPERATOR(sub, rsub, operator-, l - r) 122 | PYBIND11_BINARY_OPERATOR(add, radd, operator+, l + r) 123 | PYBIND11_BINARY_OPERATOR(mul, rmul, operator*, l * r) 124 | PYBIND11_BINARY_OPERATOR(truediv, rtruediv, operator/, l / r) 125 | PYBIND11_BINARY_OPERATOR(mod, rmod, operator%, l % r) 126 | PYBIND11_BINARY_OPERATOR(lshift, rlshift, operator<<, l << r) 127 | PYBIND11_BINARY_OPERATOR(rshift, rrshift, operator>>, l >> r) 128 | PYBIND11_BINARY_OPERATOR(and, rand, operator&, l & r) 129 | PYBIND11_BINARY_OPERATOR(xor, rxor, operator^, l ^ r) 130 | PYBIND11_BINARY_OPERATOR(eq, eq, operator==, l == r) 131 | PYBIND11_BINARY_OPERATOR(ne, ne, operator!=, l != r) 132 | PYBIND11_BINARY_OPERATOR(or , ror, operator|, l | r) 133 | PYBIND11_BINARY_OPERATOR(gt, lt, operator>, l > r) 134 | PYBIND11_BINARY_OPERATOR(ge, le, operator>=, l >= r) 135 | PYBIND11_BINARY_OPERATOR(lt, gt, operator<, l < r) 136 | PYBIND11_BINARY_OPERATOR(le, ge, operator<=, l <= r) 137 | //PYBIND11_BINARY_OPERATOR(pow, rpow, pow, std::pow(l, r)) 138 | PYBIND11_INPLACE_OPERATOR(iadd, operator+=, l += r) 139 | PYBIND11_INPLACE_OPERATOR(isub, operator-=, l -= r) 140 | PYBIND11_INPLACE_OPERATOR(imul, operator*=, l *= r) 141 | PYBIND11_INPLACE_OPERATOR(itruediv, operator/=, l /= r) 142 | PYBIND11_INPLACE_OPERATOR(imod, operator%=, l %= r) 143 | PYBIND11_INPLACE_OPERATOR(ilshift, operator<<=, l <<= r) 144 | PYBIND11_INPLACE_OPERATOR(irshift, operator>>=, l >>= r) 145 | PYBIND11_INPLACE_OPERATOR(iand, operator&=, l &= r) 146 | PYBIND11_INPLACE_OPERATOR(ixor, operator^=, l ^= r) 147 | PYBIND11_INPLACE_OPERATOR(ior, operator|=, l |= r) 148 | PYBIND11_UNARY_OPERATOR(neg, operator-, -l) 149 | PYBIND11_UNARY_OPERATOR(pos, operator+, +l) 150 | PYBIND11_UNARY_OPERATOR(abs, abs, std::abs(l)) 151 | PYBIND11_UNARY_OPERATOR(hash, hash, std::hash()(l)) 152 | PYBIND11_UNARY_OPERATOR(invert, operator~, (~l)) 153 | PYBIND11_UNARY_OPERATOR(bool, operator!, !!l) 154 | PYBIND11_UNARY_OPERATOR(int, int_, (int)l) 155 | PYBIND11_UNARY_OPERATOR(float, float_, (double)l) 156 | 157 | #undef PYBIND11_BINARY_OPERATOR 158 | #undef PYBIND11_INPLACE_OPERATOR 159 | #undef PYBIND11_UNARY_OPERATOR 160 | NAMESPACE_END(detail) 161 | 162 | using detail::self; 163 | 164 | NAMESPACE_END(PYBIND11_NAMESPACE) 165 | 166 | #if defined(_MSC_VER) 167 | # pragma warning(pop) 168 | #endif 169 | -------------------------------------------------------------------------------- /src/include/pybind11/options.h: -------------------------------------------------------------------------------- 1 | /* 2 | pybind11/options.h: global settings that are configurable at runtime. 3 | 4 | Copyright (c) 2016 Wenzel Jakob 5 | 6 | All rights reserved. Use of this source code is governed by a 7 | BSD-style license that can be found in the LICENSE file. 8 | */ 9 | 10 | #pragma once 11 | 12 | #include "detail/common.h" 13 | 14 | NAMESPACE_BEGIN(PYBIND11_NAMESPACE) 15 | 16 | class options { 17 | public: 18 | 19 | // Default RAII constructor, which leaves settings as they currently are. 20 | options() : previous_state(global_state()) {} 21 | 22 | // Class is non-copyable. 23 | options(const options&) = delete; 24 | options& operator=(const options&) = delete; 25 | 26 | // Destructor, which restores settings that were in effect before. 27 | ~options() { 28 | global_state() = previous_state; 29 | } 30 | 31 | // Setter methods (affect the global state): 32 | 33 | options& disable_user_defined_docstrings() & { global_state().show_user_defined_docstrings = false; return *this; } 34 | 35 | options& enable_user_defined_docstrings() & { global_state().show_user_defined_docstrings = true; return *this; } 36 | 37 | options& disable_function_signatures() & { global_state().show_function_signatures = false; return *this; } 38 | 39 | options& enable_function_signatures() & { global_state().show_function_signatures = true; return *this; } 40 | 41 | // Getter methods (return the global state): 42 | 43 | static bool show_user_defined_docstrings() { return global_state().show_user_defined_docstrings; } 44 | 45 | static bool show_function_signatures() { return global_state().show_function_signatures; } 46 | 47 | // This type is not meant to be allocated on the heap. 48 | void* operator new(size_t) = delete; 49 | 50 | private: 51 | 52 | struct state { 53 | bool show_user_defined_docstrings = true; //< Include user-supplied texts in docstrings. 54 | bool show_function_signatures = true; //< Include auto-generated function signatures in docstrings. 55 | }; 56 | 57 | static state &global_state() { 58 | static state instance; 59 | return instance; 60 | } 61 | 62 | state previous_state; 63 | }; 64 | 65 | NAMESPACE_END(PYBIND11_NAMESPACE) 66 | -------------------------------------------------------------------------------- /src/include/pydef.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "stdafx.h" 4 | 5 | namespace py = pybind11; 6 | void Export_pystes_Core_structs(py::module& m); 7 | void Export_pystes_Core_classes(py::module& m); 8 | void Export_pystes_TArray(py::module& m); 9 | void Export_pystes_gamedefines(py::module& m); 10 | -------------------------------------------------------------------------------- /src/include/stdafx.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define VERSION_MAJOR 0 4 | #define VERSION_MINOR 7 5 | #define VERSION_PATCH 11 6 | 7 | #include 8 | #define WIN32_LEAN_AND_MEAN 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | #include 24 | #pragma comment (lib, "Shlwapi.lib") 25 | 26 | namespace UnrealSDK 27 | { 28 | extern void* pGObjects; 29 | extern void* pGNames; 30 | extern void* pGObjHash; 31 | extern void* pGCRCTable; 32 | extern void* pNameHash; 33 | } 34 | 35 | #include "gamedefines.h" 36 | 37 | #include "logging.h" 38 | 39 | #include "UnrealEngine/Core/Core_structs.h" 40 | #include "UnrealEngine/Core/Core_f_structs.h" 41 | #include "UnrealEngine/Core/Core_classes.h" 42 | 43 | #include "UnrealEngine/Engine/Engine_structs.h" 44 | #include "UnrealEngine/Engine/Engine_f_structs.h" 45 | #include "UnrealEngine/Engine/Engine_classes.h" 46 | 47 | #include "TypeMap.h" 48 | 49 | #include "Games.h" 50 | 51 | namespace pybind11 52 | { 53 | template 54 | struct polymorphic_type_hook::value>> 55 | { 56 | static const void* get(const itype* src, const std::type_info*& type) 57 | { 58 | if (src) 59 | { 60 | if (((UObject *)src)->Class) 61 | { 62 | std::string type_name = ((UObject *)src)->Class->GetName(); 63 | if (uobject_type_map.count(type_name)) 64 | type = uobject_type_map[type_name]; 65 | } 66 | return src; 67 | } 68 | type = nullptr; 69 | return dynamic_cast(src); 70 | } 71 | }; 72 | } 73 | 74 | 75 | namespace pybind11 76 | { 77 | namespace detail 78 | { 79 | template <> 80 | struct type_caster 81 | { 82 | public: 83 | PYBIND11_TYPE_CASTER(FString, _("FString")); 84 | 85 | bool load(handle src, bool) 86 | { 87 | if (!isinstance(src)) 88 | return false; 89 | PyObject* source = src.ptr(); 90 | const wchar_t* tmp = PyUnicode_AsWideCharString(source, nullptr); 91 | if (!tmp) 92 | return false; 93 | value = FString(tmp); 94 | return true; 95 | } 96 | 97 | static handle cast(FString src, return_value_policy /* policy */, handle /* parent */) 98 | { 99 | Logging::LogD("FString cast %p %d\n", src.Data, src.Count); 100 | if (src.Data && src.Count) 101 | return PyUnicode_FromWideChar(src.Data, src.Count - 1); 102 | return none(); 103 | } 104 | }; 105 | } 106 | } 107 | 108 | namespace pybind11 109 | { 110 | namespace detail 111 | { 112 | template <> 113 | struct type_caster 114 | { 115 | using value_conv = make_caster; 116 | public: 117 | PYBIND11_TYPE_CASTER(UClass *, _("UClass")); 118 | 119 | bool load(handle src, bool) 120 | { 121 | if (!isinstance(src)) 122 | return false; 123 | PyObject* source = src.ptr(); 124 | const char* tmp = PyUnicode_AsUTF8AndSize(source, nullptr); 125 | if (!tmp) 126 | return false; 127 | value = UObject::FindClass(tmp, false); 128 | return value != nullptr; 129 | } 130 | 131 | static handle cast(UClass* src, return_value_policy policy, handle parent) 132 | { 133 | return value_conv::cast(forward_like(src), policy, parent); 134 | } 135 | }; 136 | } 137 | } 138 | 139 | 140 | namespace pybind11 141 | { 142 | namespace detail 143 | { 144 | template <> 145 | struct type_caster 146 | { 147 | public: 148 | PYBIND11_TYPE_CASTER(FName, _("FName")); 149 | 150 | bool load(handle src, bool) 151 | { 152 | if (!isinstance(src)) 153 | return false; 154 | PyObject* source = src.ptr(); 155 | const char* tmp = PyUnicode_AsUTF8AndSize(source, nullptr); 156 | if (!tmp) 157 | return false; 158 | value = FName(tmp); 159 | return true; 160 | } 161 | 162 | static handle cast(FName src, return_value_policy /* policy */, handle /* parent */) 163 | { 164 | return PyUnicode_FromString(src.GetName()); 165 | } 166 | }; 167 | } 168 | } 169 | 170 | #include "pydef.h" 171 | -------------------------------------------------------------------------------- /src/main.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bl-sdk/PythonSDK/2b5e9a733b41d84e8f2fc400cd9c244baab23ed5/src/main.cpp -------------------------------------------------------------------------------- /src/pydefs/_TArray.cpp: -------------------------------------------------------------------------------- 1 | #include "stdafx.h" 2 | // Using ======================================================================= 3 | namespace py = pybind11; 4 | 5 | // Module ====================================================================== 6 | void Export_pystes_TArray(py::module &m) 7 | { 8 | py::class_< PyTArray>(m, "PyTArray", py::dynamic_attr()); 9 | } -------------------------------------------------------------------------------- /src/pydefs/gamedefines.cpp: -------------------------------------------------------------------------------- 1 | #include "stdafx.h" 2 | // Using ======================================================================= 3 | namespace py = pybind11; 4 | 5 | // Module ====================================================================== 6 | void Export_pystes_gamedefines(py::module &m) 7 | { 8 | py::class_< FName >(m, "FName") 9 | .def(py::init<>()) 10 | .def(py::init()) 11 | .def(py::init()) 12 | .def_static("Names", &FName::Names, py::return_value_policy::reference) 13 | .def_readwrite("Index", &FName::Index) 14 | .def_readwrite("Number", &FName::Number) 15 | .def("GetName", &FName::GetName, py::return_value_policy::reference) 16 | ; 17 | py::class_< FNameEntry >(m, "FNameEntry") 18 | .def(py::init<>()) 19 | .def_readonly("Name", &FNameEntry::Name) 20 | ; 21 | py::class_< FOutputDevice >(m, "FOutputDevice") 22 | .def(py::init<>()) 23 | .def_readwrite("VfTable", &FOutputDevice::VfTable, py::return_value_policy::reference) 24 | .def_readwrite("bAllowSuppression", &FOutputDevice::bAllowSuppression, py::return_value_policy::reference) 25 | .def_readwrite("bSuppressEventTag", &FOutputDevice::bSuppressEventTag, py::return_value_policy::reference) 26 | .def_readwrite("bAutoEmitLineTerminator", &FOutputDevice::bAutoEmitLineTerminator, py::return_value_policy::reference) 27 | ; 28 | py::class_< FFrame, FOutputDevice >(m, "FFrame") 29 | .def_readwrite("Node", &FFrame::Node, py::return_value_policy::reference) 30 | .def_readwrite("Object", &FFrame::Object, py::return_value_policy::reference) 31 | .def_readwrite("PreviousFrame", &FFrame::PreviousFrame, py::return_value_policy::reference) 32 | .def_property("Code", [](FFrame &self) { return (int)self.Code; }, [](FFrame &self, int value) {self.Code = (unsigned char *)value; }) 33 | .def_property("Locals", [](FFrame &self) { return (int)self.Locals; }, [](FFrame &self, int value) {self.Locals = (unsigned char *)value; }) 34 | .def("SkipFunction", &FFrame::SkipFunction, py::return_value_policy::reference) 35 | ; 36 | 37 | py::class_< FString >(m, "FString") 38 | .def(py::init()) 39 | .def_readwrite("Count", &FString::Count) 40 | .def_readwrite("Max", &FString::Max) 41 | .def_readwrite("Data", &FString::Data) 42 | .def("AsString", &FString::AsString) 43 | ; 44 | 45 | py::class_< FScriptDelegate >(m, "FScriptDelegate") 46 | .def(py::init<>()) 47 | ; 48 | } -------------------------------------------------------------------------------- /src/stdafx.cpp: -------------------------------------------------------------------------------- 1 | #include "stdafx.h" 2 | --------------------------------------------------------------------------------