├── ndkpath.txt ├── .gitattributes ├── Application.mk ├── mod ├── logger.h ├── il2cpp.h ├── icfg.h ├── logger.cpp ├── interface.h ├── config.h ├── amlmod.h ├── iaml.h └── config.cpp ├── Android.mk ├── LICENSE ├── isautils.h ├── .gitignore └── main.cpp /ndkpath.txt: -------------------------------------------------------------------------------- 1 | D:\android-ndk -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /Application.mk: -------------------------------------------------------------------------------- 1 | APP_STL := c++_static 2 | APP_ABI := armeabi-v7a 3 | APP_OPTIM := release -------------------------------------------------------------------------------- /mod/logger.h: -------------------------------------------------------------------------------- 1 | #ifndef _LOGGER 2 | #define _LOGGER 3 | 4 | class Logger 5 | { 6 | public: 7 | Logger(); 8 | void SetTag(const char* szTag); 9 | void Info(const char* szMessage, ...); 10 | void Error(const char* szMessage, ...); 11 | static Logger* GetLogger(); 12 | private: 13 | const char* m_szTag; 14 | }; 15 | extern Logger* logger; 16 | 17 | #endif // _LOGGER -------------------------------------------------------------------------------- /Android.mk: -------------------------------------------------------------------------------- 1 | LOCAL_PATH := $(call my-dir) 2 | 3 | include $(CLEAR_VARS) 4 | LOCAL_CPP_EXTENSION := .cpp .cc 5 | LOCAL_MODULE := drawdistance 6 | LOCAL_SRC_FILES := main.cpp mod/logger.cpp mod/config.cpp 7 | LOCAL_CFLAGS += -O2 -mfloat-abi=softfp -DNDEBUG -std=c++17 8 | LOCAL_C_INCLUDES += ./include 9 | LOCAL_LDLIBS += -llog # ARM64 library requires it so... 10 | include $(BUILD_SHARED_LIBRARY) -------------------------------------------------------------------------------- /mod/il2cpp.h: -------------------------------------------------------------------------------- 1 | struct IL2Assembly; 2 | struct IL2Object; 3 | struct IL2Class; 4 | struct IL2Image; 5 | struct IL2Array; 6 | struct IL2Type; 7 | 8 | struct IL2Domain; 9 | struct IL2ReflectionType; 10 | struct IL2Exception; 11 | struct IL2Profiler; 12 | struct IL2Thread; 13 | struct IL2ReflectionMethod; 14 | struct IL2ManagedMemorySnapshot; 15 | struct IL2StackFrameInfo; 16 | struct IL2CustomAttrInfo; 17 | struct IL2GenericClass; 18 | struct IL2Defaults; 19 | 20 | struct IL2TypeDefinition; 21 | struct IL2GenericParameter; 22 | struct IL2GenericContainer; 23 | 24 | struct MethodInfo; 25 | struct FieldInfo; 26 | struct PropertyInfo; 27 | struct EventInfo; -------------------------------------------------------------------------------- /mod/icfg.h: -------------------------------------------------------------------------------- 1 | /* Is not required. Can be used only for a smaller size of mod (~370kb savings) */ 2 | /* because of fstream include (it has a lot of templates & not only) */ 3 | /* There's no reason of using this feature if you're already using fstream */ 4 | 5 | #ifndef _ICFG 6 | #define _ICFG 7 | 8 | class ICFG 9 | { 10 | public: 11 | virtual void* InitIniPointer() = 0; 12 | virtual void ParseInputStream(void* iniPointer, const char* szFilename) = 0; 13 | virtual void GenerateToOutputStream(void* iniPointer, const char* szFilename) = 0; 14 | virtual const char* GetValueFrom(void* iniPointer, const char* szSection, const char* szKey) = 0; 15 | virtual void SetValueTo(void* iniPointer, const char* szSection, const char* szKey, const char* szValue) = 0; 16 | }; 17 | 18 | extern ICFG* icfg; 19 | inline ICFG* GetCFGInterface() 20 | { 21 | return icfg; 22 | } 23 | 24 | #endif // _ICFG -------------------------------------------------------------------------------- /mod/logger.cpp: -------------------------------------------------------------------------------- 1 | #include "logger.h" 2 | #include 3 | #include 4 | #include 5 | 6 | Logger::Logger() 7 | { 8 | m_szTag = "AML Mod"; 9 | } 10 | 11 | Logger* Logger::GetLogger() 12 | { 13 | return logger; 14 | } 15 | 16 | void Logger::SetTag(const char* szTag) 17 | { 18 | m_szTag = szTag; 19 | } 20 | 21 | void Logger::Info(const char* szMessage, ...) 22 | { 23 | char buffer[384]; 24 | va_list args; 25 | va_start(args, szMessage); 26 | vsprintf(buffer, szMessage, args); 27 | __android_log_write(ANDROID_LOG_INFO, m_szTag, buffer); 28 | va_end(args); 29 | } 30 | 31 | void Logger::Error(const char* szMessage, ...) 32 | { 33 | char buffer[384]; 34 | va_list args; 35 | va_start(args, szMessage); 36 | vsprintf(buffer, szMessage, args); 37 | __android_log_write(ANDROID_LOG_ERROR, m_szTag, buffer); 38 | va_end(args); 39 | } 40 | 41 | static Logger loggerLocal; 42 | Logger* logger = &loggerLocal; -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 RusJJ 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /mod/interface.h: -------------------------------------------------------------------------------- 1 | /* DO NOT CHANGE IT */ 2 | 3 | #ifndef __GETINTERFACE_H 4 | #define __GETINTERFACE_H 5 | 6 | #if defined(_WIN32) || defined(_WIN64) 7 | #define WIN32_LEAN_AND_MEAN 8 | #include 9 | #else 10 | #include 11 | #endif 12 | #define DEFAULT_LIB_NAME "AML" 13 | 14 | #define WRAP_INTERFACE(__interface_name, __interface_var) RegisterInterface(#__interface_name, __interface_var) 15 | 16 | typedef void* (*GetInterfaceFn)(const char*); 17 | typedef void* (*RegInterfaceFn)(const char*, void*); 18 | 19 | inline void* GetInterface(const char* szInterfaceName) 20 | { 21 | #if defined(_WIN32) || defined(_WIN64) 22 | GetInterfaceFn _GetInterface = (GetInterfaceFn)GetProcAddress(GetModuleHandleA(DEFAULT_LIB_NAME ".dll"), "GetInterface"); 23 | #else 24 | GetInterfaceFn _GetInterface = (GetInterfaceFn)dlsym((void*)dlopen("lib" DEFAULT_LIB_NAME ".so", RTLD_NOW), "GetInterface"); 25 | #endif 26 | return _GetInterface(szInterfaceName); 27 | } 28 | 29 | inline void RegisterInterface(const char* szInterfaceName, void* pInterface) 30 | { 31 | #if defined(_WIN32) || defined(_WIN64) 32 | RegInterfaceFn _RegInterface = (RegInterfaceFn)GetProcAddress(GetModuleHandleA(DEFAULT_LIB_NAME ".dll"), "CreateInterface"); 33 | #else 34 | RegInterfaceFn _RegInterface = (RegInterfaceFn)dlsym((void*)dlopen("lib" DEFAULT_LIB_NAME ".so", RTLD_NOW), "CreateInterface"); 35 | #endif 36 | _RegInterface(szInterfaceName, pInterface); 37 | } 38 | 39 | #endif // __GETINTERFACE_H -------------------------------------------------------------------------------- /mod/config.h: -------------------------------------------------------------------------------- 1 | #ifndef _CONFIG 2 | #define _CONFIG 3 | 4 | /* Is not required. Can be used only for a smaller size of mod (~480kb savings) */ 5 | #include "icfg.h" 6 | 7 | class ConfigEntry; 8 | 9 | class Config 10 | { 11 | public: 12 | Config(const char* szName); 13 | void Init(); 14 | void Save(); 15 | ConfigEntry* Bind(const char* szKey, const char* szDefaultValue, const char* szSection = "Preferences"); 16 | ConfigEntry* Bind(const char* szKey, int nDefaultValue, const char* szSection = "Preferences"); 17 | ConfigEntry* Bind(const char* szKey, float flDefaultValue, const char* szSection = "Preferences"); 18 | ConfigEntry* Bind(const char* szKey, bool bDefaultValue, const char* szSection = "Preferences"); 19 | static Config* GetConfig(); 20 | private: 21 | bool m_bInitialized; 22 | const char* m_szName; 23 | void* m_iniMyConfig; 24 | 25 | #ifdef _ICFG 26 | /* Built-in optimizer think he's best! Ha-ha... Not funny. It's 3AM... */ 27 | ICFG* m_pICFG; 28 | #endif 29 | friend class ConfigEntry; 30 | }; 31 | 32 | class ConfigEntry 33 | { 34 | public: 35 | void SetString(const char* newValue); 36 | inline const char* GetString() { return m_szValue; } 37 | void SetFloat(float newValue); 38 | inline float GetFloat() { return m_fFloatValue; } 39 | void SetBool(bool newValue); 40 | inline bool GetBool() { return m_nIntegerValue; } 41 | void SetInt(int newValue); 42 | inline int GetInt() { return m_nIntegerValue; } 43 | private: 44 | Config* m_pBoundCfg; 45 | const char* m_szMySection; 46 | const char* m_szMyKey; 47 | const char* m_szValue; 48 | float m_fFloatValue; 49 | int m_nIntegerValue; 50 | 51 | friend class Config; 52 | }; 53 | extern Config* cfg; 54 | 55 | #endif // _CONFIG -------------------------------------------------------------------------------- /isautils.h: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | typedef void (*OnSettingChangedFn)(int nOldValue, int nNewValue); 4 | typedef const char* (*OnSettingDrawedFn)(int nNewValue); 5 | 6 | enum eTypeOfSettings : unsigned char 7 | { 8 | Controller = 0, 9 | Game = 1, 10 | Display = 2, 11 | Audio = 3, 12 | Language = 4, 13 | }; 14 | 15 | class ISAUtils 16 | { 17 | public: 18 | /* Functions below added in 1.0.0.0 */ 19 | 20 | /** Get an address of Fastman Limit Adjuster if included 21 | * 22 | * \return Address of a FLA library 23 | */ 24 | virtual uintptr_t IsFLALoaded() = 0; 25 | 26 | // switchesArray is an array of items of clickable item (isSlider = false) 27 | #if __cplusplus >= 201300 // Do not create errors on C++11 and lower :P 28 | [[deprecated("Use AddClickableItem or AddSliderItem")]] 29 | #endif 30 | virtual int AddSettingsItem(eTypeOfSettings typeOf, const char* name, int initVal = 0, int minVal = 0, int maxVal = 0, OnSettingChangedFn fnOnValueChange = nullptr, bool isSlider = false, void* switchesArray = nullptr) = 0; 31 | 32 | /** Get a value of setting (returned by AddClickableItem or AddSliderItem) 33 | * 34 | * \param settingId Numeric ID of setting (0-200 for example) 35 | * \return Value of that setting 36 | */ 37 | virtual int ValueOfSettingsItem(int settingId) = 0; 38 | 39 | /* Functions below added in 1.1.0.0 */ 40 | 41 | /** Adds clickable text button in menu settings 42 | * 43 | * \param typeOf In which setting option that item should be added 44 | * \param name Obviously a displayed name 45 | * \param initVal Initial value (def: 0) 46 | * \param minVal Minimum value (def: 0) 47 | * \param maxVal Maximum value (def: 0) 48 | * \param switchesArray An array that includes names of options 49 | * \param fnOnValueChange A function that will be called on value being saved (def: null) 50 | * \return Setting ID 51 | */ 52 | virtual int AddClickableItem(eTypeOfSettings typeOf, const char* name, int initVal = 0, int minVal = 0, int maxVal = 0, const char** switchesArray = nullptr, OnSettingChangedFn fnOnValueChange = nullptr) = 0; 53 | 54 | /** Adds a slider in menu settings 55 | * 56 | * \param typeOf In which setting option that item should be added 57 | * \param name Obviously a displayed name 58 | * \param initVal Initial value (def: 0) 59 | * \param minVal Minimum value (def: 0) 60 | * \param maxVal Maximum value (def: 0) 61 | * \param fnOnValueChange A function that will be called on value being saved (def: null) 62 | * \param fnOnValueDraw A function that will control a text of a slider (def: null) 63 | * \return Setting ID 64 | */ 65 | virtual int AddSliderItem(eTypeOfSettings typeOf, const char* name, int initVal = 0, int minVal = 0, int maxVal = 0, OnSettingChangedFn fnOnValueChange = nullptr, OnSettingDrawedFn fnOnValueDraw = nullptr) = 0; 66 | }; -------------------------------------------------------------------------------- /mod/amlmod.h: -------------------------------------------------------------------------------- 1 | #ifndef _AMLMOD 2 | #define _AMLMOD 3 | 4 | #include "iaml.h" 5 | #include 6 | #include 7 | 8 | #ifdef __clang__ 9 | #define TARGET_ARM __attribute__((target("no-thumb-mode"))) 10 | #define TARGET_THUMB __attribute__((target("thumb-mode"))) 11 | #endif 12 | 13 | #ifdef __GNUC__ 14 | #define ASM_NAKED __attribute__((naked)) 15 | #else 16 | #define ASM_NAKED __declspec(naked) 17 | #endif 18 | 19 | #define MYMOD(_guid, _name, _version, _author) \ 20 | static ModInfo modinfoLocal(#_guid, #_name, #_version, #_author); \ 21 | ModInfo* modinfo = &modinfoLocal; \ 22 | extern "C" ModInfo* __GetModInfo() { return modinfo; } \ 23 | IAML* aml = (IAML*)GetInterface("AMLInterface"); 24 | 25 | #define MYMODCFG(_guid, _name, _version, _author) \ 26 | static ModInfo modinfoLocal(#_guid, #_name, #_version, #_author); \ 27 | ModInfo* modinfo = &modinfoLocal; \ 28 | extern "C" ModInfo* __GetModInfo() { return modinfo; } \ 29 | IAML* aml = (IAML*)GetInterface("AMLInterface"); \ 30 | static Config cfgLocal(#_guid); \ 31 | Config* cfg = &cfgLocal; 32 | 33 | #define NEEDGAME(_pkg_name) \ 34 | extern "C" const char* __INeedASpecificGame() {return #_pkg_name;} 35 | 36 | #define MYMODDECL() \ 37 | extern ModInfo* modinfo; // Just in case if you need to use that somewhere else in your mod 38 | 39 | /* Dependencies! */ 40 | #define BEGIN_DEPLIST() \ 41 | static ModInfoDependency g_listDependencies[] = { 42 | 43 | #define ADD_DEPENDENCY(_guid) \ 44 | {#_guid, ""}, 45 | 46 | #define ADD_DEPENDENCY_VER(_guid, _version) \ 47 | {#_guid, #_version}, 48 | 49 | #define END_DEPLIST() \ 50 | {"", ""} }; \ 51 | extern "C" ModInfoDependency* __GetDepsList() { return &g_listDependencies[0]; } 52 | 53 | 54 | 55 | struct ModInfoDependency 56 | { 57 | const char* szGUID; 58 | const char* szVersion; 59 | }; 60 | 61 | class ModInfo 62 | { 63 | public: 64 | ModInfo(const char* szGUID, const char* szName, const char* szVersion, const char* szAuthor) 65 | { 66 | /* No buffer overflow! */ 67 | strncpy(this->szGUID, szGUID, sizeof(ModInfo::szGUID)); this->szGUID[sizeof(ModInfo::szGUID) - 1] = '\0'; 68 | strncpy(this->szName, szName, sizeof(ModInfo::szName)); this->szName[sizeof(ModInfo::szName) - 1] = '\0'; 69 | strncpy(this->szVersion, szVersion, sizeof(ModInfo::szVersion)); this->szVersion[sizeof(ModInfo::szVersion) - 1] = '\0'; 70 | strncpy(this->szAuthor, szAuthor, sizeof(ModInfo::szAuthor)); this->szAuthor[sizeof(ModInfo::szAuthor) - 1] = '\0'; 71 | 72 | /* GUID should be lowcase */ 73 | for(int i = 0; this->szGUID[i] != '\0'; ++i) 74 | { 75 | this->szGUID[i] = tolower((int)(this->szGUID[i])); 76 | } 77 | 78 | /* Parse version string */ 79 | if(sscanf(this->szVersion, "%hu.%hu.%hu.%hu", &major, &minor, &revision, &build) < 4) 80 | { 81 | if(sscanf(this->szVersion, "%hu.%hu.%hu", &major, &minor, &revision) < 3) 82 | { 83 | if(sscanf(this->szVersion, "%hu.%hu", &major, &minor) < 2) 84 | { 85 | major = (unsigned short)atoi(this->szVersion); 86 | } 87 | revision = 0; 88 | } 89 | build = 0; 90 | } 91 | } 92 | inline const char* GUID() { return szGUID; } 93 | inline const char* Name() { return szName; } 94 | inline const char* VersionString() { return szVersion; } 95 | inline const char* Author() { return szAuthor; } 96 | inline unsigned short Major() { return major; } 97 | inline unsigned short Minor() { return minor; } 98 | inline unsigned short Revision() { return revision; } 99 | inline unsigned short Build() { return build; } 100 | inline uintptr_t Handle() { return handle; } 101 | private: 102 | char szGUID[48]; 103 | char szName[48]; 104 | char szVersion[24]; 105 | char szAuthor[48]; 106 | unsigned short major; 107 | unsigned short minor; 108 | unsigned short revision; 109 | unsigned short build; 110 | uintptr_t handle; 111 | ModInfoDependency* dependencies; 112 | 113 | friend class ModsList; 114 | }; 115 | 116 | typedef ModInfo* (*GetModInfoFn)(); 117 | 118 | #endif // _AMLMOD -------------------------------------------------------------------------------- /mod/iaml.h: -------------------------------------------------------------------------------- 1 | #ifndef _IAML 2 | #define _IAML 3 | 4 | #include "interface.h" 5 | #include 6 | 7 | #ifndef PAGE_SIZE 8 | #define PAGE_SIZE 4096 9 | #endif 10 | 11 | class IAML 12 | { 13 | public: 14 | /* AML 1.0.0 */ 15 | virtual const char* GetCurrentGame() = 0; 16 | virtual const char* GetConfigPath() = 0; 17 | virtual bool HasMod(const char* szGUID) = 0; 18 | virtual bool HasModOfVersion(const char* szGUID, const char* szVersion) = 0; 19 | virtual uintptr_t GetLib(const char* szLib) = 0; 20 | virtual uintptr_t GetSym(uintptr_t handle, const char* sym) = 0; 21 | virtual void Hook(void* handle, void* fnAddress, void** orgFnAddress = nullptr) = 0; 22 | virtual void HookPLT(void* handle, void* fnAddress, void** orgFnAddress = nullptr) = 0; 23 | virtual int Unprot(uintptr_t handle, size_t len = PAGE_SIZE) = 0; 24 | virtual void Write(uintptr_t dest, uintptr_t src, size_t size) = 0; 25 | virtual void Read(uintptr_t src, uintptr_t dest, size_t size) = 0; 26 | virtual void PlaceNOP(uintptr_t addr, size_t count = 1) = 0; // Untested on ARMv8 27 | virtual void PlaceJMP(uintptr_t addr, uintptr_t dest) = 0; // Untested on ARMv8 28 | virtual void PlaceRET(uintptr_t addr) = 0; // Untested on ARMv8 29 | }; 30 | 31 | extern IAML* aml; 32 | inline IAML* GetAMLInterface() 33 | { 34 | return aml; 35 | } 36 | 37 | 38 | /* Unprotect that memory chunk for making changes */ 39 | #define UNPROT(_addr, ...) \ 40 | aml->Unprot((uintptr_t)(_addr), ( __VA_ARGS__ )); 41 | /* Just write own info to the memory */ 42 | #define WRITE(_addr, _whatToWrite, _size) \ 43 | aml->Write(_addr, _whatToWrite, _size); 44 | 45 | /* Just a hook declaration */ 46 | #define DECL_HOOK(_ret, _name, ...) \ 47 | _ret (*_name)(__VA_ARGS__); \ 48 | _ret HookOf_##_name(__VA_ARGS__) 49 | /* Just a hook declaration with return type = void */ 50 | #define DECL_HOOKv(_name, ...) \ 51 | void (*_name)(__VA_ARGS__); \ 52 | void HookOf_##_name(__VA_ARGS__) 53 | /* Just a hook of a function */ 54 | #define HOOK(_name, _fnAddr) \ 55 | aml->Hook((void*)(_fnAddr), (void*)(&HookOf_##_name), (void**)(&_name)); 56 | /* Just a hook of a function located in PLT section (by address!) */ 57 | #define HOOKPLT(_name, _fnAddr) \ 58 | aml->HookPLT((void*)(_fnAddr), (void*)(&HookOf_##_name), (void**)(&_name)); 59 | /* Just a hook of a function hidden behind IL2CPP */ 60 | #define HOOK_IL2CPP(_name, _methodInfo) \ 61 | aml->Hook((void*)_methodInfo->methodPointer, (void*)(&HookOf_##_name), (void**)(&_name)); 62 | /* Unhook a function (unsafe, actually) */ 63 | #define UNHOOK(_name, _fnAddr) \ 64 | aml->Hook((void*)(_fnAddr), (void*)(&_name), (void**)0); 65 | /* Unhook an IL2CPP function (unsafe, actually) */ 66 | #define UNHOOK_IL2CPP(_name, _methodInfo) \ 67 | aml->Hook((void*)_methodInfo->methodPointer, (void*)(&_name), (void**)0); 68 | 69 | /* Just a hook decl with saveable original function pointer */ 70 | #define DECL_HOOK2(_ret, _name, ...) \ 71 | _ret (*_name)(__VA_ARGS__); \ 72 | static void* fnLocated##_name = 0; \ 73 | _ret HookOf_##_name(__VA_ARGS__) 74 | /* Just a hook declaration with return type = void and saveable original function pointer */ 75 | #define DECL_HOOK2v(_name, ...) \ 76 | void (*_name)(__VA_ARGS__); \ 77 | static void* fnLocated##_name = 0; \ 78 | void HookOf_##_name(__VA_ARGS__) 79 | /* Just a hook of a function and save original function pointer */ 80 | #define HOOK2(_name, _fnAddr) \ 81 | fnLocated##_name = (void*)_fnAddr; \ 82 | aml->Hook((void*)(_fnAddr), (void*)(&HookOf_##_name), (void**)(&_name)); 83 | /* Just a hook of a function located in PLT section (by address!) and save original function pointer */ 84 | #define HOOK2PLT(_name, _fnAddr) \ 85 | fnLocated##_name = (void*)_fnAddr; \ 86 | aml->HookPLT((void*)(_fnAddr), (void*)(&HookOf_##_name), (void**)(&_name)); 87 | /* Just a hook of a function hidden behind IL2CPP and save original function pointer */ 88 | #define HOOK2_IL2CPP(_name, _methodInfo) \ 89 | fnLocated##_name = (void*)_methodInfo->methodPointer; \ 90 | aml->Hook((void*)_methodInfo->methodPointer, (void*)(&HookOf_##_name), (void**)(&_name)); 91 | /* Unhook a function (unsafe, actually) that was saved before */ 92 | #define UNHOOK2(_name) \ 93 | aml->Hook(fnLocated##_name, (void*)(&_name), (void**)0); 94 | 95 | #endif // _IAML -------------------------------------------------------------------------------- /mod/config.cpp: -------------------------------------------------------------------------------- 1 | #include "config.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include "amlmod.h" 8 | #include "iaml.h" 9 | #if !defined(__AML) && defined(_ICFG) 10 | ICFG* icfg; 11 | #else 12 | #include 13 | #include "thirdparty/inipp.h" 14 | #endif 15 | #ifdef __AML 16 | extern std::string g_szCfgPath; 17 | #endif 18 | 19 | 20 | 21 | extern ModInfo* modinfo; 22 | 23 | Config::Config(const char* szName) 24 | { 25 | #if !defined(__AML) && defined(_ICFG) 26 | m_pICFG = (ICFG*)GetInterface("AMLConfig"); 27 | m_iniMyConfig = m_pICFG->InitIniPointer(); 28 | #else 29 | m_iniMyConfig = new inipp::Ini(); 30 | #endif 31 | m_bInitialized = false; 32 | m_szName = szName; 33 | 34 | #ifndef __AML 35 | Init(); 36 | #endif 37 | } 38 | 39 | void Config::Init() 40 | { 41 | if(m_bInitialized) return; 42 | m_bInitialized = true; 43 | #if !defined(__AML) && defined(_ICFG) 44 | m_pICFG->ParseInputStream(m_iniMyConfig, m_szName); 45 | #else 46 | #ifdef __AML 47 | std::ifstream cfgStream((g_szCfgPath + m_szName + ".ini").c_str()); 48 | #else 49 | std::ifstream cfgStream((std::string(aml->GetConfigPath()) + m_szName + ".ini").c_str()); 50 | #endif 51 | if(cfgStream.is_open()) 52 | { 53 | ((inipp::Ini*)m_iniMyConfig)->parse(cfgStream); 54 | } 55 | cfgStream.close(); 56 | #endif 57 | } 58 | 59 | void Config::Save() 60 | { 61 | if(!m_bInitialized) return; 62 | #if !defined(__AML) && defined(_ICFG) 63 | m_pICFG->GenerateToOutputStream(m_iniMyConfig, m_szName); 64 | #else 65 | #ifdef __AML 66 | std::ofstream cfgStream((g_szCfgPath + m_szName + ".ini").c_str()); 67 | #else 68 | std::ofstream cfgStream((std::string(aml->GetConfigPath()) + m_szName + ".ini").c_str()); 69 | #endif 70 | if(cfgStream.is_open()) 71 | { 72 | ((inipp::Ini*)m_iniMyConfig)->generate(cfgStream); 73 | } 74 | cfgStream << ""; 75 | cfgStream.close(); 76 | #endif 77 | } 78 | 79 | ConfigEntry* Config::Bind(const char* szKey, const char* szDefaultValue, const char* szSection) 80 | { 81 | if(!m_bInitialized) return nullptr; 82 | ConfigEntry* pRet = new ConfigEntry; 83 | pRet->m_pBoundCfg = this; 84 | pRet->m_szMySection = szSection; 85 | pRet->m_szMyKey = szKey; 86 | const char* tryToGetValue; 87 | #if !defined(__AML) && defined(_ICFG) 88 | tryToGetValue = m_pICFG->GetValueFrom(m_iniMyConfig, szSection, szKey); 89 | #else 90 | tryToGetValue = ((inipp::Ini*)m_iniMyConfig)->sections[szSection][szKey].c_str(); 91 | #endif 92 | if(tryToGetValue[0] == '\0') 93 | pRet->SetString(szDefaultValue); 94 | else 95 | pRet->SetString(tryToGetValue); 96 | Save(); 97 | return pRet; 98 | } 99 | 100 | ConfigEntry* Config::Bind(const char* szKey, int nDefaultValue, const char* szSection) 101 | { 102 | if(!m_bInitialized) return nullptr; 103 | ConfigEntry* pRet = new ConfigEntry; 104 | pRet->m_pBoundCfg = this; 105 | pRet->m_szMySection = szSection; 106 | pRet->m_szMyKey = szKey; 107 | const char* tryToGetValue; 108 | #if !defined(__AML) && defined(_ICFG) 109 | tryToGetValue = m_pICFG->GetValueFrom(m_iniMyConfig, szSection, szKey); 110 | #else 111 | tryToGetValue = ((inipp::Ini*)m_iniMyConfig)->sections[szSection][szKey].c_str(); 112 | #endif 113 | if(tryToGetValue[0] == '\0') 114 | pRet->SetInt(nDefaultValue); 115 | else 116 | pRet->SetString(tryToGetValue); 117 | Save(); 118 | return pRet; 119 | } 120 | 121 | ConfigEntry* Config::Bind(const char* szKey, float flDefaultValue, const char* szSection) 122 | { 123 | if(!m_bInitialized) return nullptr; 124 | ConfigEntry* pRet = new ConfigEntry; 125 | pRet->m_pBoundCfg = this; 126 | pRet->m_szMySection = szSection; 127 | pRet->m_szMyKey = szKey; 128 | const char* tryToGetValue; 129 | #if !defined(__AML) && defined(_ICFG) 130 | tryToGetValue = m_pICFG->GetValueFrom(m_iniMyConfig, szSection, szKey); 131 | #else 132 | tryToGetValue = ((inipp::Ini*)m_iniMyConfig)->sections[szSection][szKey].c_str(); 133 | #endif 134 | if(tryToGetValue[0] == '\0') 135 | pRet->SetFloat(flDefaultValue); 136 | else 137 | pRet->SetString(tryToGetValue); 138 | Save(); 139 | return pRet; 140 | } 141 | 142 | ConfigEntry* Config::Bind(const char* szKey, bool bDefaultValue, const char* szSection) 143 | { 144 | if(!m_bInitialized) return nullptr; 145 | ConfigEntry* pRet = new ConfigEntry; 146 | pRet->m_pBoundCfg = this; 147 | pRet->m_szMySection = szSection; 148 | pRet->m_szMyKey = szKey; 149 | const char* tryToGetValue; 150 | #if !defined(__AML) && defined(_ICFG) 151 | tryToGetValue = m_pICFG->GetValueFrom(m_iniMyConfig, szSection, szKey); 152 | #else 153 | tryToGetValue = ((inipp::Ini*)m_iniMyConfig)->sections[szSection][szKey].c_str(); 154 | #endif 155 | if(tryToGetValue[0] == '\0') 156 | pRet->SetBool(bDefaultValue); 157 | else 158 | pRet->SetString(tryToGetValue); 159 | Save(); 160 | return pRet; 161 | } 162 | 163 | void ConfigEntry::SetString(const char* newValue) 164 | { 165 | m_szValue = newValue; 166 | m_nIntegerValue = atoi(m_szValue); 167 | m_fFloatValue = (float)atof(m_szValue); 168 | 169 | #if !defined(__AML) && defined(_ICFG) 170 | m_pBoundCfg->m_pICFG->SetValueTo(m_pBoundCfg->m_iniMyConfig, m_szMySection, m_szMyKey, m_szValue); 171 | #else 172 | ((inipp::Ini*)(m_pBoundCfg->m_iniMyConfig))->sections[m_szMySection][m_szMyKey] = m_szValue; 173 | #endif 174 | } 175 | 176 | void ConfigEntry::SetFloat(float newValue) 177 | { 178 | m_fFloatValue = newValue; 179 | m_nIntegerValue = (int)newValue; 180 | 181 | char szVal[32]; 182 | sprintf(szVal, "%f", newValue); 183 | m_szValue = szVal; 184 | 185 | #if !defined(__AML) && defined(_ICFG) 186 | m_pBoundCfg->m_pICFG->SetValueTo(m_pBoundCfg->m_iniMyConfig, m_szMySection, m_szMyKey, m_szValue); 187 | #else 188 | ((inipp::Ini*)(m_pBoundCfg->m_iniMyConfig))->sections[m_szMySection][m_szMyKey] = m_szValue; 189 | #endif 190 | } 191 | 192 | void ConfigEntry::SetInt(int newValue) 193 | { 194 | m_fFloatValue = (float)newValue; 195 | m_nIntegerValue = newValue; 196 | 197 | char szVal[32]; 198 | sprintf(szVal, "%d", newValue); 199 | m_szValue = szVal; 200 | 201 | #if !defined(__AML) && defined(_ICFG) 202 | m_pBoundCfg->m_pICFG->SetValueTo(m_pBoundCfg->m_iniMyConfig, m_szMySection, m_szMyKey, m_szValue); 203 | #else 204 | ((inipp::Ini*)(m_pBoundCfg->m_iniMyConfig))->sections[m_szMySection][m_szMyKey] = m_szValue; 205 | #endif 206 | } 207 | 208 | void ConfigEntry::SetBool(bool newValue) 209 | { 210 | m_fFloatValue = newValue?1.0f:0.0f; 211 | m_nIntegerValue = newValue?1:0; 212 | m_szValue = newValue?"1":"0"; 213 | 214 | #if !defined(__AML) && defined(_ICFG) 215 | m_pBoundCfg->m_pICFG->SetValueTo(m_pBoundCfg->m_iniMyConfig, m_szMySection, m_szMyKey, m_szValue); 216 | #else 217 | ((inipp::Ini*)(m_pBoundCfg->m_iniMyConfig))->sections[m_szMySection][m_szMyKey] = m_szValue; 218 | #endif 219 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 5 | 6 | # User-specific files 7 | *.rsuser 8 | *.suo 9 | *.user 10 | *.userosscache 11 | *.sln.docstates 12 | 13 | # User-specific files (MonoDevelop/Xamarin Studio) 14 | *.userprefs 15 | 16 | # Build results 17 | [Dd]ebug/ 18 | [Dd]ebugPublic/ 19 | [Rr]elease/ 20 | [Rr]eleases/ 21 | x64/ 22 | x86/ 23 | bld/ 24 | [Bb]in/ 25 | [Oo]bj/ 26 | [Ll]og/ 27 | 28 | # Visual Studio 2015/2017 cache/options directory 29 | .vs/ 30 | # Uncomment if you have tasks that create the project's static files in wwwroot 31 | #wwwroot/ 32 | 33 | # Visual Studio 2017 auto generated files 34 | Generated\ Files/ 35 | 36 | # MSTest test Results 37 | [Tt]est[Rr]esult*/ 38 | [Bb]uild[Ll]og.* 39 | 40 | # NUNIT 41 | *.VisualState.xml 42 | TestResult.xml 43 | 44 | # Build Results of an ATL Project 45 | [Dd]ebugPS/ 46 | [Rr]eleasePS/ 47 | dlldata.c 48 | 49 | # Benchmark Results 50 | BenchmarkDotNet.Artifacts/ 51 | 52 | # .NET Core 53 | project.lock.json 54 | project.fragment.lock.json 55 | artifacts/ 56 | 57 | # StyleCop 58 | StyleCopReport.xml 59 | 60 | # Files built by Visual Studio 61 | *_i.c 62 | *_p.c 63 | *_h.h 64 | *.ilk 65 | *.meta 66 | *.obj 67 | *.iobj 68 | *.pch 69 | *.pdb 70 | *.ipdb 71 | *.pgc 72 | *.pgd 73 | *.rsp 74 | *.sbr 75 | *.tlb 76 | *.tli 77 | *.tlh 78 | *.tmp 79 | *.tmp_proj 80 | *_wpftmp.csproj 81 | *.log 82 | *.vspscc 83 | *.vssscc 84 | .builds 85 | *.pidb 86 | *.svclog 87 | *.scc 88 | 89 | # Chutzpah Test files 90 | _Chutzpah* 91 | 92 | # Visual C++ cache files 93 | ipch/ 94 | *.aps 95 | *.ncb 96 | *.opendb 97 | *.opensdf 98 | *.sdf 99 | *.cachefile 100 | *.VC.db 101 | *.VC.VC.opendb 102 | 103 | # Visual Studio profiler 104 | *.psess 105 | *.vsp 106 | *.vspx 107 | *.sap 108 | 109 | # Visual Studio Trace Files 110 | *.e2e 111 | 112 | # TFS 2012 Local Workspace 113 | $tf/ 114 | 115 | # Guidance Automation Toolkit 116 | *.gpState 117 | 118 | # ReSharper is a .NET coding add-in 119 | _ReSharper*/ 120 | *.[Rr]e[Ss]harper 121 | *.DotSettings.user 122 | 123 | # JustCode is a .NET coding add-in 124 | .JustCode 125 | 126 | # TeamCity is a build add-in 127 | _TeamCity* 128 | 129 | # DotCover is a Code Coverage Tool 130 | *.dotCover 131 | 132 | # AxoCover is a Code Coverage Tool 133 | .axoCover/* 134 | !.axoCover/settings.json 135 | 136 | # Visual Studio code coverage results 137 | *.coverage 138 | *.coveragexml 139 | 140 | # NCrunch 141 | _NCrunch_* 142 | .*crunch*.local.xml 143 | nCrunchTemp_* 144 | 145 | # MightyMoose 146 | *.mm.* 147 | AutoTest.Net/ 148 | 149 | # Web workbench (sass) 150 | .sass-cache/ 151 | 152 | # Installshield output folder 153 | [Ee]xpress/ 154 | 155 | # DocProject is a documentation generator add-in 156 | DocProject/buildhelp/ 157 | DocProject/Help/*.HxT 158 | DocProject/Help/*.HxC 159 | DocProject/Help/*.hhc 160 | DocProject/Help/*.hhk 161 | DocProject/Help/*.hhp 162 | DocProject/Help/Html2 163 | DocProject/Help/html 164 | 165 | # Click-Once directory 166 | publish/ 167 | 168 | # Publish Web Output 169 | *.[Pp]ublish.xml 170 | *.azurePubxml 171 | # Note: Comment the next line if you want to checkin your web deploy settings, 172 | # but database connection strings (with potential passwords) will be unencrypted 173 | *.pubxml 174 | *.publishproj 175 | 176 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 177 | # checkin your Azure Web App publish settings, but sensitive information contained 178 | # in these scripts will be unencrypted 179 | PublishScripts/ 180 | 181 | # NuGet Packages 182 | *.nupkg 183 | # The packages folder can be ignored because of Package Restore 184 | **/[Pp]ackages/* 185 | # except build/, which is used as an MSBuild target. 186 | !**/[Pp]ackages/build/ 187 | # Uncomment if necessary however generally it will be regenerated when needed 188 | #!**/[Pp]ackages/repositories.config 189 | # NuGet v3's project.json files produces more ignorable files 190 | *.nuget.props 191 | *.nuget.targets 192 | 193 | # Microsoft Azure Build Output 194 | csx/ 195 | *.build.csdef 196 | 197 | # Microsoft Azure Emulator 198 | ecf/ 199 | rcf/ 200 | 201 | # Windows Store app package directories and files 202 | AppPackages/ 203 | BundleArtifacts/ 204 | Package.StoreAssociation.xml 205 | _pkginfo.txt 206 | *.appx 207 | 208 | # Visual Studio cache files 209 | # files ending in .cache can be ignored 210 | *.[Cc]ache 211 | # but keep track of directories ending in .cache 212 | !*.[Cc]ache/ 213 | 214 | # Others 215 | ClientBin/ 216 | ~$* 217 | *~ 218 | *.dbmdl 219 | *.dbproj.schemaview 220 | *.jfm 221 | *.pfx 222 | *.publishsettings 223 | orleans.codegen.cs 224 | 225 | # Including strong name files can present a security risk 226 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 227 | #*.snk 228 | 229 | # Since there are multiple workflows, uncomment next line to ignore bower_components 230 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 231 | #bower_components/ 232 | 233 | # RIA/Silverlight projects 234 | Generated_Code/ 235 | 236 | # Backup & report files from converting an old project file 237 | # to a newer Visual Studio version. Backup files are not needed, 238 | # because we have git ;-) 239 | _UpgradeReport_Files/ 240 | Backup*/ 241 | UpgradeLog*.XML 242 | UpgradeLog*.htm 243 | ServiceFabricBackup/ 244 | *.rptproj.bak 245 | 246 | # SQL Server files 247 | *.mdf 248 | *.ldf 249 | *.ndf 250 | 251 | # Business Intelligence projects 252 | *.rdl.data 253 | *.bim.layout 254 | *.bim_*.settings 255 | *.rptproj.rsuser 256 | 257 | # Microsoft Fakes 258 | FakesAssemblies/ 259 | 260 | # GhostDoc plugin setting file 261 | *.GhostDoc.xml 262 | 263 | # Node.js Tools for Visual Studio 264 | .ntvs_analysis.dat 265 | node_modules/ 266 | 267 | # Visual Studio 6 build log 268 | *.plg 269 | 270 | # Visual Studio 6 workspace options file 271 | *.opt 272 | 273 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 274 | *.vbw 275 | 276 | # Visual Studio LightSwitch build output 277 | **/*.HTMLClient/GeneratedArtifacts 278 | **/*.DesktopClient/GeneratedArtifacts 279 | **/*.DesktopClient/ModelManifest.xml 280 | **/*.Server/GeneratedArtifacts 281 | **/*.Server/ModelManifest.xml 282 | _Pvt_Extensions 283 | 284 | # Paket dependency manager 285 | .paket/paket.exe 286 | paket-files/ 287 | 288 | # FAKE - F# Make 289 | .fake/ 290 | 291 | # JetBrains Rider 292 | .idea/ 293 | *.sln.iml 294 | 295 | # CodeRush personal settings 296 | .cr/personal 297 | 298 | # Python Tools for Visual Studio (PTVS) 299 | __pycache__/ 300 | *.pyc 301 | 302 | # Cake - Uncomment if you are using it 303 | # tools/** 304 | # !tools/packages.config 305 | 306 | # Tabs Studio 307 | *.tss 308 | 309 | # Telerik's JustMock configuration file 310 | *.jmconfig 311 | 312 | # BizTalk build output 313 | *.btp.cs 314 | *.btm.cs 315 | *.odx.cs 316 | *.xsd.cs 317 | 318 | # OpenCover UI analysis results 319 | OpenCover/ 320 | 321 | # Azure Stream Analytics local run output 322 | ASALocalRun/ 323 | 324 | # MSBuild Binary and Structured Log 325 | *.binlog 326 | 327 | # NVidia Nsight GPU debugger configuration file 328 | *.nvuser 329 | 330 | # MFractors (Xamarin productivity tool) working folder 331 | .mfractor/ 332 | 333 | # Local History for Visual Studio 334 | .localhistory/ 335 | -------------------------------------------------------------------------------- /main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "isautils.h" 6 | ISAUtils* sautils = nullptr; 7 | 8 | #define THUMB_ADDRESS(_address) ((_address) | 1) 9 | 10 | MYMODCFG(net.rusjj.gtasa.drawdistance, GTA:SA Draw Distance, 1.1, RusJJ) 11 | NEEDGAME(com.rockstargames.gtasa) 12 | BEGIN_DEPLIST() 13 | ADD_DEPENDENCY_VER(net.rusjj.aml, 1.0) 14 | END_DEPLIST() 15 | 16 | /* Saves */ 17 | uintptr_t pGTASA = 0; 18 | unsigned int nStreamingMemoryOverride; 19 | unsigned int nWaterBlocksToRender = 0; 20 | uintptr_t pointerWaterBlocksToRender = 0; 21 | float fRealAspectRatio = 0.0f; 22 | float fAspectRatioScaler = 0.0f; 23 | float flDontGetCfgValueEveryTick = 0.0f; 24 | 25 | /* Configs */ 26 | ConfigEntry* pNearClipOverride; 27 | ConfigEntry* pDrawDistanceOverride; 28 | ConfigEntry* pStreamingDistanceScale; 29 | ConfigEntry* pHiWaterDistanceScale; 30 | 31 | /* Lib Pointers */ 32 | void* maincamera; // RwCamera* 33 | int* streamingMemoryAvailable; 34 | void* gMobileMenu; 35 | float* CameraRangeMinX; 36 | float* CameraRangeMaxX; 37 | float* CameraRangeMinY; 38 | float* CameraRangeMaxY; 39 | 40 | /* Realloc */ 41 | int16_t* pBlocksToBeRenderedOutsideWorldX; 42 | int16_t* pBlocksToBeRenderedOutsideWorldY; 43 | 44 | /* Functions Pointers */ 45 | typedef float (*RetFloatFn)(); 46 | 47 | DECL_HOOK(void*, RwCameraSetNearClipPlane, void* self, float a1) 48 | { 49 | if(self == maincamera && pNearClipOverride->GetFloat() > 0.0f) 50 | { 51 | return RwCameraSetNearClipPlane(self, pNearClipOverride->GetFloat()); 52 | } 53 | return RwCameraSetNearClipPlane(self, a1); 54 | } 55 | DECL_HOOK(void*, RwCameraSetFarClipPlane, void* self, float a1) 56 | { 57 | if(self == maincamera && pDrawDistanceOverride->GetInt() > 200) 58 | return RwCameraSetFarClipPlane(self, pDrawDistanceOverride->GetFloat()); 59 | return RwCameraSetFarClipPlane(self, a1); 60 | } 61 | 62 | DECL_HOOK(void*, CameraCreate, void* a1, void* a2, int a3) 63 | { 64 | maincamera = CameraCreate(a1, a2, a3); 65 | return maincamera; 66 | } 67 | 68 | DECL_HOOK(void*, CameraProcess, uintptr_t self) 69 | { 70 | fAspectRatioScaler = fRealAspectRatio * 0.75 * pStreamingDistanceScale->GetFloat(); // AspectRatio / 4:3 71 | 72 | void* ret = CameraProcess(self); 73 | *(float*)(self + 236) *= fAspectRatioScaler; 74 | *(float*)(self + 240) *= fAspectRatioScaler; 75 | return ret; 76 | } 77 | 78 | DECL_HOOK(void*, CStreamingUpdate, void* self) 79 | { 80 | if(*streamingMemoryAvailable < nStreamingMemoryOverride) 81 | *streamingMemoryAvailable = nStreamingMemoryOverride; 82 | return CStreamingUpdate(self); 83 | } 84 | 85 | // SAUtils 86 | #define DEFAULT_DRAWDISTANCE 800.0f 87 | char szRetDrawDistanceSlider[12]; 88 | void RealDrawDistanceChanged(int oldVal, int newVal) 89 | { 90 | pDrawDistanceOverride->SetInt(newVal); 91 | cfg->Save(); 92 | } 93 | const char* RealDrawDistanceDraw(int nNewValue) 94 | { 95 | sprintf(szRetDrawDistanceSlider, "x%.2f", (nNewValue / DEFAULT_DRAWDISTANCE)); 96 | return szRetDrawDistanceSlider; 97 | } 98 | void StreamingDistanceChanged(int oldVal, int newVal) 99 | { 100 | pStreamingDistanceScale->SetFloat(0.01f * newVal); 101 | cfg->Save(); 102 | } 103 | const char* StreamingDistanceDraw(int nNewValue) 104 | { 105 | sprintf(szRetDrawDistanceSlider, "x%.2f", (nNewValue / 100.0f)); 106 | return szRetDrawDistanceSlider; 107 | } 108 | void HiWaterDistanceChanged(int oldVal, int newVal) 109 | { 110 | pHiWaterDistanceScale->SetFloat(0.01f * newVal); 111 | flDontGetCfgValueEveryTick = pHiWaterDistanceScale->GetFloat(); 112 | cfg->Save(); 113 | } 114 | const char* HiWaterDistanceDraw(int nNewValue) 115 | { 116 | sprintf(szRetDrawDistanceSlider, "%d%%", nNewValue); 117 | return szRetDrawDistanceSlider; 118 | } 119 | 120 | TARGET_THUMB ASM_NAKED void HitWaterBlock_JMP() 121 | { 122 | __asm( 123 | ".hidden nWaterBlocksToRender\n" 124 | ".hidden pointerWaterBlocksToRender\n" 125 | ".thumb\n" 126 | "PUSH {R1}\n" // Backup R1 127 | "LDR R1, =(nWaterBlocksToRender - 100001f - 2*(100002f-100001f))\n" // Copy contents of variable to R0 128 | "100001:\nADD R1, PC\n100002:\n" 129 | "LDR R1, [R1]\n" 130 | 131 | "CMP R0, R1\n" // Compare R0=nWaterBlocksToRender with R1=m_NumBlocksOutsideWorldToBeRendered 132 | "POP {R1}\n" // Bring back R1 133 | 134 | "IT GT\n" 135 | "POPGT {R4,R5,R7,PC}\n" 136 | "LDR R1, =(0x6777C4 - 0x59869C)\n" 137 | 138 | "PUSH {R0,R1}\n" 139 | "LDR R0, =(pointerWaterBlocksToRender - 100001f - 2*(100002f-100001f))\n" // Copy contents of variable to R0 140 | "100001:\nADD R0, PC\n100002:\n" 141 | "LDR R0, [R0]\n" 142 | "STR R0, [SP, #4]\n" 143 | "POP {R0,PC}\n" 144 | ); 145 | } 146 | 147 | void CodeRedirect(uintptr_t address, uintptr_t newAddress, bool isThumb) 148 | { 149 | if(isThumb) 150 | { 151 | char code[12]; 152 | unsigned int sizeOfData = 0; 153 | 154 | if (address % 4 == 0) 155 | { 156 | *(uint32_t*)(code + 0) = 0xF000F8DF; 157 | *(const void**)(code + 4) = (const void*)newAddress; 158 | sizeOfData = 8; 159 | } 160 | else 161 | { 162 | *(uint32_t*)(code + 0) = 0xBF00; 163 | *(uint32_t*)(code + 2) = 0xF000F8DF; 164 | *(const void**)(code + 6) = (const void*)newAddress; 165 | sizeOfData = 10; 166 | } 167 | aml->Write(address, (uintptr_t)code, sizeOfData); 168 | return; 169 | } 170 | 171 | char code[8]; 172 | *(uint32_t*)(code + 0) = 0xE51FF004; 173 | *(const void**)(code + 4) = (const void*)newAddress; 174 | aml->Write(address, (uintptr_t)code, sizeof(code)); 175 | } 176 | 177 | DECL_HOOK(void, emu_SetWater, bool set) 178 | { 179 | emu_SetWater(set); 180 | if(set) 181 | { 182 | //*CameraRangeMinX *= flDontGetCfgValueEveryTick; 183 | *CameraRangeMaxX *= flDontGetCfgValueEveryTick; 184 | //*CameraRangeMinY *= flDontGetCfgValueEveryTick; 185 | *CameraRangeMaxY *= flDontGetCfgValueEveryTick; 186 | } 187 | } 188 | 189 | extern "C" void OnModLoad() 190 | { 191 | logger->SetTag("GTASA Draw Distance"); 192 | pGTASA = aml->GetLib("libGTASA.so"); 193 | 194 | pNearClipOverride = cfg->Bind("NearClip", "0.1"); 195 | pDrawDistanceOverride = cfg->Bind("DrawDistance", "800.0"); 196 | nStreamingMemoryOverride = cfg->Bind("PreferredStreamingMemMB", "1024")->GetInt() * 1024 * 1024; 197 | nWaterBlocksToRender = (unsigned int)cfg->Bind("WaterBlocksToRender", "384")->GetInt(); 198 | pStreamingDistanceScale = cfg->Bind("StreamingDistanceScale", "1.0"); 199 | 200 | fRealAspectRatio = ((RetFloatFn)(pGTASA + 0x18E984))(); 201 | if(fRealAspectRatio < 1.0f) fRealAspectRatio = 1.0f / fRealAspectRatio; 202 | streamingMemoryAvailable = (int*)(pGTASA + 0x685FA0); 203 | 204 | gMobileMenu = (void*)(pGTASA + 0x6E006C); 205 | 206 | if(nWaterBlocksToRender > 1) 207 | { 208 | if(nWaterBlocksToRender > 70) // Default value is 0-69 (70) so don`t override if not necessary 209 | { 210 | pBlocksToBeRenderedOutsideWorldX = new int16_t[nWaterBlocksToRender]; 211 | aml->Write(pGTASA + 0x6777C4, (uintptr_t)&pBlocksToBeRenderedOutsideWorldX, sizeof(void*)); 212 | 213 | pBlocksToBeRenderedOutsideWorldY = new int16_t[nWaterBlocksToRender]; 214 | aml->Write(pGTASA + 0xA1C05C, (uintptr_t)&pBlocksToBeRenderedOutsideWorldY, sizeof(void*)); 215 | } 216 | --nWaterBlocksToRender; 217 | pointerWaterBlocksToRender = (pGTASA + 0x598694) | 1; 218 | CodeRedirect(pGTASA + 0x59868C, (uintptr_t)&HitWaterBlock_JMP, true); // From FLA 219 | } 220 | 221 | HOOKPLT(RwCameraSetNearClipPlane, pGTASA + 0x670C9C); 222 | HOOKPLT(RwCameraSetFarClipPlane, pGTASA + 0x6740CC); 223 | 224 | HOOKPLT(CameraCreate, pGTASA + 0x675174); 225 | HOOKPLT(CameraProcess, pGTASA + 0x6717BC); 226 | 227 | HOOKPLT(CStreamingUpdate, pGTASA + 0x673898); 228 | 229 | if(cfg->Bind("MoreOftenPopulationUpdate", "1")->GetBool()) 230 | { 231 | aml->Unprot(pGTASA + 0x3F40C0, sizeof(char)); 232 | *(char*)(pGTASA + 0x3F40C0) = 2; 233 | } 234 | 235 | // Do not delete vehicles behind the player camera 236 | bool bRemoveCarsBehind = cfg->Bind("DontRemoveVehicleBehindCamera", "1")->GetBool(); 237 | if(bRemoveCarsBehind) 238 | { 239 | aml->PlaceJMP(pGTASA + 0x2EC660, pGTASA + 0x2EC6D6); 240 | } 241 | 242 | // Do not delete peds behind the player camera 243 | if(cfg->Bind("DontRemovePedBehindCamera", "1")->GetBool()) 244 | { 245 | aml->PlaceJMP(pGTASA + 0x4CE4EA, pGTASA + 0x4CE55C); 246 | } 247 | 248 | if(cfg->Bind("SpawnVehiclesInFrontOfPlayer", "1")->GetBool()) 249 | { 250 | if(bRemoveCarsBehind) // They are already spawning! 251 | { 252 | //aml->Unprot(pGTASA + 0x2E866F, sizeof(char)); 253 | //*(char*)(pGTASA + 0x2E866F) = 0xDB; 254 | } 255 | else 256 | { 257 | aml->PlaceJMP(pGTASA + 0x2E864E, pGTASA + 0x2E8670); 258 | } 259 | } 260 | else 261 | { 262 | if(bRemoveCarsBehind) // Huh? 263 | { 264 | aml->Unprot(pGTASA + 0x2E866F, sizeof(char)); 265 | *(char*)(pGTASA + 0x2E866F) = 0xDB; 266 | } 267 | } 268 | 269 | /* Unstable + Useless */ 270 | //pHiWaterDistanceScale = cfg->Bind("HighWaterDistanceScale", 1.0f); 271 | //flDontGetCfgValueEveryTick = pHiWaterDistanceScale->GetFloat(); 272 | //CameraRangeMinX = (float*)(pGTASA + 0xA1DC8C); 273 | //CameraRangeMaxX = (float*)(pGTASA + 0xA1DC90); 274 | //CameraRangeMinY = (float*)(pGTASA + 0xA1DC94); 275 | //CameraRangeMaxY = (float*)(pGTASA + 0xA1DC98); 276 | //HOOKPLT(emu_SetWater, pGTASA + 0x673530); 277 | 278 | sautils = (ISAUtils*)GetInterface("SAUtils"); 279 | if(sautils != nullptr) 280 | { 281 | sautils->AddSliderItem(Display, "Real Draw Distance", pDrawDistanceOverride->GetInt(), 200, 4000, RealDrawDistanceChanged, RealDrawDistanceDraw); 282 | sautils->AddSliderItem(Display, "Streaming Distance Scale", 100 * pStreamingDistanceScale->GetFloat(), 100 * 0.25f, 100 * 5.0f, StreamingDistanceChanged, StreamingDistanceDraw); 283 | //sautils->AddSliderItem(Display, "Detailed Water Draw Distance", 100 * pHiWaterDistanceScale->GetFloat(), 0, 100 * 5.0f, HiWaterDistanceChanged, HiWaterDistanceDraw); 284 | } 285 | } --------------------------------------------------------------------------------