├── .gitignore ├── .gitmodules ├── README.md ├── premake5.bat ├── premake5.exe ├── premake5.lua ├── resources ├── SA │ └── models │ │ ├── fronten1.txd │ │ └── CONTROLLER_PS2.png │ │ └── hud.txd │ │ ├── SkipHigh.png │ │ └── SkipIcon.png └── VC │ └── fronten2.txd │ ├── fe_arrows1.png │ ├── fe_arrows2.png │ ├── fe_arrows3.png │ ├── fe_arrows4.png │ └── fe_controller.png └── source ├── Colors.h ├── GInputAPI.h ├── LoadingScreen.cpp ├── LoadingScreen.h ├── Main.cpp ├── ModuleList.hpp ├── Settings.cpp ├── Settings.h ├── SkyUIAPI.h └── Utility.h /.gitignore: -------------------------------------------------------------------------------- 1 | # Folders 2 | output/ 3 | project_files/** 4 | *.pdb 5 | *.asi 6 | *.ilk -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "vendor/ModUtils"] 2 | path = vendor/ModUtils 3 | url = https://github.com/CookiePLMonster/ModUtils 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # [SkyUI](https://gtaforums.com/topic/899738-skyui/) 2 | PS2 like main menu for GTA: III/Vice City and San Andreas. 3 | 4 | ## Compatibility: 5 | - III version 1.0 only 6 | - VC version 1.0 only 7 | - SA version 1.0 only 8 | 9 | ## Features: 10 | - PS2 like menu for all games from the classic trilogy. 11 | - [MenuMap](https://github.com/gennariarmando/menu-map) compatibility (III/VC only). 12 | - Fully functional gallery (SA only). 13 | - PS2 like trip skip icon (SA only). 14 | 15 | ## Screenshots: 16 |

17 | 18 | 19 | 20 |

21 | 22 | ## Compiling: 23 | Requirements: 24 | - Visual Studio 2022 25 | - [Plugin SDK](https://github.com/DK22Pac/plugin-sdk) 26 | 27 | ## Download: 28 | Download the latest archive from the [releases](https://github.com/gennariarmando/sky-ui/releases) page. 29 | 30 | # Installation: 31 | #### Installing an ASI Loader: 32 | An ASI Loader is required in order to inject the plugin into the game, if you already have one skip to the next step.\ 33 | Recommended: [Ultimate ASI Loader](https://github.com/ThirteenAG/Ultimate-ASI-Loader) 34 | 35 | #### Installing SkyUI: 36 | Create a folder called "scripts" inside your GTA directory and paste both SkyUI.asi and SkyUI.ini in it. 37 | 38 | ## Links: 39 | - [plugin-sdk](https://github.com/DK22Pac/plugin-sdk) 40 | - [stb](https://github.com/nothings/stb) 41 | -------------------------------------------------------------------------------- /premake5.bat: -------------------------------------------------------------------------------- 1 | premake5 vs2022 -------------------------------------------------------------------------------- /premake5.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gennariarmando/sky-ui/8278de2c407526238e5f3eb69e5c6afbd8e569d7/premake5.exe -------------------------------------------------------------------------------- /premake5.lua: -------------------------------------------------------------------------------- 1 | workspace "sky-ui" 2 | configurations { "ReleaseSA", "DebugSA", "ReleaseVC", "DebugVC", "ReleaseIII", "DebugIII" } 3 | location "project_files" 4 | 5 | project "sky-ui" 6 | kind "SharedLib" 7 | language "C++" 8 | targetdir "output/asi/" 9 | objdir ("output/obj") 10 | targetextension ".asi" 11 | characterset ("MBCS") 12 | linkoptions "/SAFESEH:NO" 13 | buildoptions { "-std:c++latest", "/permissive" } 14 | defines { "_CRT_SECURE_NO_WARNINGS", "_CRT_NON_CONFORMING_SWPRINTFS", "_USE_MATH_DEFINES", "RW" } 15 | disablewarnings { "4244", "4800", "4305", "4073", "4838", "4996", "4221", "4430", "26812", "26495", "6031" } 16 | 17 | files { 18 | "source/**.*", 19 | } 20 | 21 | includedirs { 22 | "source/**", 23 | } 24 | 25 | includedirs { 26 | "$(PLUGIN_SDK_DIR)/shared/", 27 | "$(PLUGIN_SDK_DIR)/shared/game/", 28 | } 29 | 30 | filter { "configurations:*III" } 31 | defines { "GTA3", "PLUGIN_SGV_10EN" } 32 | includedirs { 33 | "$(PLUGIN_SDK_DIR)/plugin_III/", 34 | "$(PLUGIN_SDK_DIR)/plugin_III/game_III/", 35 | "$(PLUGIN_SDK_DIR)/plugin_III/game_III/rw/", 36 | "$(RWD3D9_DIR)/source" 37 | } 38 | targetname "SkyUI" 39 | debugdir "$(GTA_III_DIR)" 40 | debugcommand "$(GTA_III_DIR)/gta3.exe" 41 | postbuildcommands "copy /y \"$(TargetPath)\" \"$(GTA_III_DIR)\\scripts\\SkyUI.asi\"" 42 | 43 | filter { "configurations:*VC" } 44 | defines { "GTAVC", "PLUGIN_SGV_10EN" } 45 | includedirs { 46 | "$(PLUGIN_SDK_DIR)/plugin_vc/", 47 | "$(PLUGIN_SDK_DIR)/plugin_vc/game_vc/", 48 | "$(PLUGIN_SDK_DIR)/plugin_vc/game_vc/rw/", 49 | "$(RWD3D9_DIR)/source" 50 | } 51 | targetname "SkyUI" 52 | debugdir "$(GTA_VC_DIR)" 53 | debugcommand "$(GTA_VC_DIR)/gta-vc.exe" 54 | postbuildcommands "copy /y \"$(TargetPath)\" \"$(GTA_VC_DIR)\\scripts\\SkyUI.asi\"" 55 | 56 | filter { "configurations:*SA" } 57 | defines { "GTASA", "PLUGIN_SGV_10US" } 58 | includedirs { 59 | "$(PLUGIN_SDK_DIR)/plugin_sa/", 60 | "$(PLUGIN_SDK_DIR)/plugin_sa/game_sa/", 61 | "$(PLUGIN_SDK_DIR)/plugin_sa/game_sa/rw" 62 | } 63 | targetname "SkyUI" 64 | debugdir "$(GTA_SA_DIR)" 65 | debugcommand "$(GTA_SA_DIR)/gta-sa.exe" 66 | postbuildcommands "copy /y \"$(TargetPath)\" \"$(GTA_SA_DIR)\\scripts\\SkyUI.asi\"" 67 | 68 | filter { } 69 | 70 | libdirs { 71 | "$(PLUGIN_SDK_DIR)/output/lib/", 72 | "$(PLUGIN_SDK_DIR)\\shared\\bass", 73 | } 74 | 75 | filter "configurations:Debug*" 76 | defines { "DEBUG" } 77 | symbols "on" 78 | staticruntime "on" 79 | 80 | filter "configurations:Release*" 81 | defines { "NDEBUG" } 82 | symbols "off" 83 | optimize "On" 84 | staticruntime "on" 85 | 86 | filter "configurations:ReleaseSA" 87 | links { "plugin" } 88 | filter "configurations:ReleaseVC" 89 | links { "plugin_vc" } 90 | filter "configurations:ReleaseIII" 91 | links { "plugin_iii" } 92 | 93 | filter "configurations:DebugSA" 94 | links { "plugin_d" } 95 | filter "configurations:DebugVC" 96 | links { "plugin_vc_d" } 97 | filter "configurations:DebugIII" 98 | links { "plugin_iii_d" } 99 | 100 | filter { } 101 | -------------------------------------------------------------------------------- /resources/SA/models/fronten1.txd/CONTROLLER_PS2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gennariarmando/sky-ui/8278de2c407526238e5f3eb69e5c6afbd8e569d7/resources/SA/models/fronten1.txd/CONTROLLER_PS2.png -------------------------------------------------------------------------------- /resources/SA/models/hud.txd/SkipHigh.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gennariarmando/sky-ui/8278de2c407526238e5f3eb69e5c6afbd8e569d7/resources/SA/models/hud.txd/SkipHigh.png -------------------------------------------------------------------------------- /resources/SA/models/hud.txd/SkipIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gennariarmando/sky-ui/8278de2c407526238e5f3eb69e5c6afbd8e569d7/resources/SA/models/hud.txd/SkipIcon.png -------------------------------------------------------------------------------- /resources/VC/fronten2.txd/fe_arrows1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gennariarmando/sky-ui/8278de2c407526238e5f3eb69e5c6afbd8e569d7/resources/VC/fronten2.txd/fe_arrows1.png -------------------------------------------------------------------------------- /resources/VC/fronten2.txd/fe_arrows2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gennariarmando/sky-ui/8278de2c407526238e5f3eb69e5c6afbd8e569d7/resources/VC/fronten2.txd/fe_arrows2.png -------------------------------------------------------------------------------- /resources/VC/fronten2.txd/fe_arrows3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gennariarmando/sky-ui/8278de2c407526238e5f3eb69e5c6afbd8e569d7/resources/VC/fronten2.txd/fe_arrows3.png -------------------------------------------------------------------------------- /resources/VC/fronten2.txd/fe_arrows4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gennariarmando/sky-ui/8278de2c407526238e5f3eb69e5c6afbd8e569d7/resources/VC/fronten2.txd/fe_arrows4.png -------------------------------------------------------------------------------- /resources/VC/fronten2.txd/fe_controller.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gennariarmando/sky-ui/8278de2c407526238e5f3eb69e5c6afbd8e569d7/resources/VC/fronten2.txd/fe_controller.png -------------------------------------------------------------------------------- /source/Colors.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define HUD_COLOUR_RED_LC01 219, 36, 38 4 | #define HUD_COLOUR_RED 205, 5, 5 5 | #define HUD_COLOUR_GREENLIGHT 25, 130, 70 6 | #define HUD_COLOUR_ORANGE 144, 98, 16 7 | #define HUD_COLOUR_ORANGELIGHT 210, 150, 25 8 | #define HUD_COLOUR_WHITE 225, 225, 225 9 | #define HUD_COLOUR_PINK 225, 225, 225 10 | #define HUD_COLOUR_BLUELIGHT 86, 196, 255 11 | #define HUD_COLOUR_BLUEDARK 20, 94, 136 12 | #define HUD_COLOUR_BLUEWHITE 164, 196, 232 13 | #define HUD_COLOUR_AZURE 49, 101, 148 14 | #define HUD_COLOUR_AZUREDARK 80, 100, 123 15 | #define HUD_COLOUR_GREY 150, 150, 150 16 | #define HUD_COLOUR_GREYDARK 96, 96, 96 17 | #define HUD_COLOUR_BLACK 0, 0, 0 18 | #define HUD_COLOUR_GREYLIGHT 205, 205, 205 19 | #define HUD_COLOUR_LCS_GREY 14, 16, 21 20 | #define HUD_COLOUR_LCS_MENU 111, 165, 208 21 | #define HUD_COLOUR_YELLOW_LIGHT 255, 235, 150 22 | -------------------------------------------------------------------------------- /source/GInputAPI.h: -------------------------------------------------------------------------------- 1 | #ifndef __GINPUTAPI 2 | #define __GINPUTAPI 3 | 4 | // GInput 1.11 API file 5 | 6 | // With this little API prepared for C++, you can take advantage of some GInput features 7 | // For functions list, scroll down to the interface declaration 8 | 9 | 10 | // As this API file is compatible with GInputIII. GInputVC and GInputSA, you need to declare 11 | // whether to compile III version, VC version, SA version, or a cross-compatible version 12 | 13 | // The API doesn't target a specific game by default - you need one of those four defines in order for API to work correctly: 14 | 15 | // To target GTA III, define GINPUT_COMPILE_III_VERSION in your project settings, code headers or just 16 | // uncomment the line below 17 | //#define GINPUT_COMPILE_III_VERSION 18 | 19 | // To target GTA VC, define GINPUT_COMPILE_VC_VERSION in your project settings, code headers or just 20 | // uncomment the line below 21 | //#define GINPUT_COMPILE_VC_VERSION 22 | 23 | // To target GTA SA, define GINPUT_COMPILE_SA_VERSION in your project settings, code headers or just 24 | // uncomment the line below 25 | //#define GINPUT_COMPILE_SA_VERSION 26 | 27 | // You can also target all three games at once by defining GINPUT_COMPILE_CROSSCOMPATIBLE_VERSION - define it 28 | // in your project settings, code headers or just uncomment the line below 29 | #define GINPUT_COMPILE_CROSSCOMPATIBLE_VERSION 30 | 31 | enum eGInputEvent 32 | { 33 | GINPUT_NO_EVENT = -1, // DON'T USE, internal use only 34 | 35 | GINPUT_EVENT_CHANGE_RADIO_STATION = 0, // Returns NULL all the time, pParam specifies whether to set 36 | // next or previous radio station (0 for next station, 1 for previous) 37 | // Callable from SendEvent: YES 38 | // Callable from SendConstEvent: NO 39 | 40 | GINPUT_EVENT_CHANGE_USER_TRACK = 1, // Returns NULL all the time, pParam specifies whether to set 41 | // next or previous MP3 (0 for next MP3, 1 for previous) 42 | // Callable from SendEvent: YES 43 | // Callable from SendConstEvent: NO 44 | 45 | GINPUT_EVENT_REGISTER_SETTINGS_RELOAD_CALLBACK = 2, // Registers a callback function which gets called after GInput*.ini gets reloaded 46 | // after WM_SETFOCUS gets called. Returns NULL. pParam should be a callback function 47 | // pointer (see GInputOnSettingsReloadCallback) 48 | // Cannot be recursive - the callbacks registered from within a callback will be ignored 49 | // Callable from SendEvent: YES 50 | // Callable from SendConstEvent: NO 51 | 52 | GINPUT_EVENT_FETCH_SETTINGS = 3, // DEPRECATED EVENT, DO NOT USE 53 | 54 | GINPUT_EVENT_FETCH_CHEAT_STRING = 4, // Copies the current cheat string to a char array sent via pParam. 55 | // pParam should point to an array of at least GINPUT_CHEAT_STRING_LENGTH characters 56 | // or a buffer overflow will occur. 57 | // Returns a value of type BOOL - when set to TRUE, cheat string has been changed since 58 | // the last frame - useful for determining when to re-check for a custom cheat combo 59 | // Letters from a cheat string are assigned to buttons as follows: 60 | // U - DPad Up, D - DPad Down, L - DPad Left, R - DPad Right 61 | // T - Y/Triangle, C - B/Circle, X - A/Cross, S - X/Square 62 | // 1 - L1/LB, 2 - L2/LT, 3 - R1/RB, 4 - R2/RT 63 | // Callable from SendEvent: YES 64 | // Callable from SendConstEvent: YES 65 | 66 | GINPUT_EVENT_RELOAD_WEAPON = 5, // Reloads the weapon player is holding 67 | // Supported only in SA 68 | // Callable from SendEvent: YES 69 | // Callable from SendConstEvent: NO 70 | 71 | GINPUT_EVENT_FETCH_GENERAL_SETTINGS = 6, // Fetches a structure filled with GInput general settings 72 | // pParam should point to a GINPUT_GENERAL_SETTINGS structure 73 | // Structure's cbSize field should equal sizeof(GINPUT_GENERAL_SETTINGS) 74 | // Callable from SendEvent: YES 75 | // Callable from SendConstEvent: YES 76 | 77 | GINPUT_EVENT_FETCH_PAD_SETTINGS = 7, // Fetches a structure filled with GInput ped-specific settings 78 | // pParam should point to a GINPUT_PAD_SETTINGS structure 79 | // Structure's cbSize field should equal sizeof(GINPUT_PAD_SETTINGS) 80 | // Callable from SendEvent: YES 81 | // Callable from SendConstEvent: YES 82 | 83 | GINPUT_EVENT_FETCH_SIXAXIS_INPUT = 8, // Fetches a structure filled with last frame's Sixaxis input 84 | // pParam should point to a SIXAXIS_INPUT structure 85 | // If Sixaxis is not supported, this struct will always contain zeroes 86 | // Callable from SendEvent: YES 87 | // Callable from SendConstEvent: YES 88 | 89 | GINPUT_EVENT_REGISTER_SIXAXIS_FETCH_CALLBACK = 9, // Registers a callback function which gets called after Sixaxis sends input 90 | // pParam should be a callback function pointer (see GInputOnSixaxisFetchCallback) 91 | // Cannot be recursive - the callbacks registered from within a callback will be ignored 92 | // Callable from SendEvent: YES 93 | // Callable from SendConstEvent: NO 94 | 95 | GINPUT_EVENT_IS_USING_SCP_DRIVER_CAPABILITIES = 10, // Checks if the game uses the pressure sensitive buttons feature, exclusive to 96 | // SCP Driver Package. 97 | // Returns BOOL - if TRUE, both SCP's xinput1_3.dll and a controller with pressure sensitive 98 | // face buttons (ie. DualShock 3) is used, FALSE otherwise. 99 | // Can be also used to determine whether Sixaxis is available or not 100 | // Callable from SendEvent: YES 101 | // Callable from SendConstEvent: YES 102 | 103 | GINPUT_EVENT_OVERRIDE_SIXAXIS_SETTINGS = 11, // Overrides all Sixaxis toggles. 104 | // if pParam is set to TRUE, the override gets enabled. If pParam is set to FALSE, 105 | // the override gets disabled. 106 | // Returns NULL all the time 107 | // Callable from SendEvent: YES 108 | // Callable from SendConstEvent: NO 109 | 110 | GINPUT_EVENT_FORCE_MAP_PAD_ONE_TO_PAD_TWO = 12, // Forcibly maps XInput pad 2 to game pad 1, same as "MapPadOneToPadTwo" INI option 111 | // if pParam is set to TRUE, the override gets enabled. If pParam is set to FALSE, 112 | // the override gets disabled. 113 | // Returns NULL all the time 114 | // Callable from SendEvent: YES 115 | // Callable from SendConstEvent: NO 116 | 117 | NUM_GINPUT_EVENTS 118 | }; 119 | 120 | struct SIXAXIS_INPUT 121 | { 122 | short ACCEL_X; 123 | short ACCEL_Y; 124 | short ACCEL_Z; 125 | 126 | short GYRO; 127 | }; 128 | 129 | // Those are options from [GInput] section 130 | // The options not present in the particular game are always 0 131 | struct GINPUT_GENERAL_SETTINGS 132 | { 133 | DWORD cbSize; // Fill with sizeof(GINPUT_GENERAL_SETTINGS) 134 | 135 | bool DisableOnFocusLost : 1; 136 | bool Vibration : 1; 137 | bool CheatsFromPad : 1; 138 | bool GuideLaunchesOverlay : 1; 139 | bool ApplyMissionSpecificFixes : 1; 140 | bool ApplyGXTFixes : 1; 141 | bool PlayStationButtons : 1; 142 | bool MapPadOneToPadTwo : 1; 143 | bool FreeAim : 1; 144 | 145 | }; 146 | 147 | // Those are options from [PadX] section 148 | // The options not present in the particular game are always 0 149 | struct GINPUT_PAD_SETTINGS 150 | { 151 | DWORD cbSize; // Fill with sizeof(GINPUT_PAD_SETTINGS) 152 | 153 | BYTE ControlsSet : 4; 154 | bool Southpaw : 1; 155 | bool SAStyleSniperZoom : 1; 156 | bool SwapSticksDuringAiming : 1; 157 | bool DrivebyWithAnalog : 1; 158 | bool HotkeyToDriveby : 1; 159 | bool InvertLook : 1; 160 | 161 | bool InvertLeftXAxis : 1; 162 | bool InvertLeftYAxis : 1; 163 | bool SwapLeftAxes : 1; 164 | float LeftStickDeadzone; 165 | float LeftStickSensitivity; 166 | 167 | bool InvertRightXAxis : 1; 168 | bool InvertRightYAxis : 1; 169 | bool SwapRightAxes : 1; 170 | float RightStickDeadzone; 171 | float RightStickSensitivity; 172 | 173 | float FaceButtonsSensitivity; 174 | float SixaxisSensitivity; 175 | bool SixaxisReloading : 1; 176 | bool SixaxisCarSteering : 1; 177 | bool SixaxisBikeSteering : 1; 178 | bool SixaxisBoatSteering : 1; 179 | bool SixaxisHeliSteering : 1; 180 | bool SixaxisPlaneSteering : 1; 181 | bool SixaxisHydraulics : 1; 182 | }; 183 | 184 | // The size of a char array returned from GINPUT_FETCH_CHEAT_STRING 185 | #define GINPUT_CHEAT_STRING_LENGTH 12 186 | 187 | // Callback called on INI file reload 188 | // Note - it is NOT called the first time INIs are loaded 189 | typedef void (*GInputOnSettingsReloadCallback)(); 190 | 191 | // Callback called when Sixaxis data gets fetched and processed by GInput 192 | // Gets called ONLY on input change 193 | typedef void (CALLBACK *GInputOnSixaxisFetchCallback)(const SIXAXIS_INPUT&); 194 | 195 | 196 | 197 | // Internal declarations 198 | #ifndef GINPUT_COMPILE_CROSSCOMPATIBLE_VERSION 199 | #if defined GINPUT_COMPILE_III_VERSION 200 | #define GINPUT_FILENAMEA "GInputIII" 201 | #define GINPUT_FILENAMEW L"GInputIII" 202 | #elif defined GINPUT_COMPILE_VC_VERSION 203 | #define GINPUT_FILENAMEA "GInputVC" 204 | #define GINPUT_FILENAMEW L"GInputVC" 205 | #elif defined GINPUT_COMPILE_SA_VERSION 206 | #define GINPUT_FILENAMEA "GInputSA" 207 | #define GINPUT_FILENAMEW L"GInputSA" 208 | #endif 209 | 210 | #ifdef UNICODE 211 | #define GINPUT_FILENAME GINPUT_FILENAMEW 212 | #else 213 | #define GINPUT_FILENAME GINPUT_FILENAMEA 214 | #endif 215 | 216 | #endif 217 | 218 | #define GINPUT_MODVERSION 0x00010B00 // 1.11 219 | 220 | // The pad interface 221 | // The interface is safe to use without validating a pointer - in case of GInput loading failure, 222 | // these functions are set to return false all the time 223 | // NOTE: Do not use any of these functions before GInput_Load is called on your interface pointer! 224 | class IGInputPad 225 | { 226 | protected: 227 | virtual ~IGInputPad() { }; 228 | 229 | public: 230 | // Returns true when XInput compatible pad is connected 231 | virtual bool IsPadConnected() const =0; 232 | 233 | // Returns true when last input came from a pad, false otherwise 234 | virtual bool HasPadInHands() const =0; 235 | 236 | // Returns installed GInput version (see GINPUT_MODVERSION), -1 on failure 237 | virtual int GetVersion() const =0; 238 | 239 | // Sends an event to GInput, allowing the script to take advantage of GInput features not available 240 | // through CPad or other original GTA functions 241 | // See eGInputEvent enum for supported events and their params/return values (if any) 242 | virtual void* SendEvent(eGInputEvent eEvent, void* pParam)=0; 243 | virtual void* SendConstEvent(eGInputEvent eEvent, void* pParam) const =0; 244 | }; 245 | 246 | // GInput management functions 247 | 248 | // Use one of those functions ONCE to initialise GInput API 249 | // DO NOT use both functions in the same plugin! 250 | // DO NOT CALL THOSE IN DLLMAIN, SINCE GINPUT MIGHT NOT BE INITIALIZED YET AT THE TIME YOUR PLUGIN LOADS 251 | // Takes a pointer to pad interface pointer as an argument, returns true if succeed and false otherwise 252 | // (GInput not installed or any other error occured) 253 | bool GInput_Load(IGInputPad** pInterfacePtr); 254 | // Takes a pointer to an array of TWO interface pointers as an argument, returns true if succeed and false otherwise 255 | // (GInput not installed or any other error occured) 256 | bool GInput_Load_TwoPads(IGInputPad** pInterfacePtr); 257 | 258 | // Releases GInput API 259 | // Call it when your program terminates 260 | void GInput_Release(); 261 | 262 | 263 | // Management functions definitions - internal use only, do not change anything here 264 | #include 265 | #include "ModuleList.hpp" 266 | 267 | // GInput ASI handle 268 | inline HMODULE* _GInput_HandlePtr() 269 | { 270 | // Making it a static variable outside of the function would duplicate it for each .cpp file which uses any API funcs (bad) 271 | static HMODULE hGInputHandle = nullptr; 272 | return &hGInputHandle; 273 | } 274 | 275 | // Although these functions may not be inlined, we need to declare them as so 276 | inline IGInputPad* _GInput_SafeMode() 277 | { 278 | static class CDummyPad : public IGInputPad 279 | { 280 | public: 281 | virtual bool IsPadConnected() const { return false; }; 282 | virtual bool HasPadInHands() const { return false; }; 283 | virtual int GetVersion() const { return -1; }; 284 | virtual void* SendEvent(eGInputEvent eEvent, void* pParam) { return CDummyPad::SendConstEvent(eEvent, pParam); }; 285 | virtual void* SendConstEvent(eGInputEvent eEvent, void* pParam) const { 286 | UNREFERENCED_PARAMETER(eEvent); UNREFERENCED_PARAMETER(pParam); 287 | SetLastError(ERROR_NOT_SUPPORTED); 288 | return reinterpret_cast(GINPUT_NO_EVENT); 289 | }; 290 | } DummyClass; 291 | return &DummyClass; 292 | } 293 | 294 | inline bool GInput_Load(IGInputPad** pInterfacePtr) 295 | { 296 | static IGInputPad* pCopiedPtr = nullptr; // We keep a backup of the interface pointer in case user calls GInput_Load multiple times 297 | static bool bLoadingResult = false; // Loading result is also cached so GInput_Load always returns the same value when called multiple times 298 | 299 | // Have we attempted to load GInput already? If so, just return a valid interface pointer and return 300 | // The pointer can be either a GInput interface or a dummy, 'safe-mode' interface which got initialised 301 | // due to GInput*.asi loading failure 302 | if ( pCopiedPtr != nullptr ) 303 | { 304 | *pInterfacePtr = pCopiedPtr; 305 | return bLoadingResult; 306 | } 307 | 308 | #ifdef GINPUT_COMPILE_CROSSCOMPATIBLE_VERSION 309 | for ( const HMODULE hHandle : ModuleList().GetAllByPrefix( L"GInput" ) ) 310 | #else 311 | const HMODULE hHandle = ModuleList().Get( GINPUT_FILENAMEW ); 312 | #endif 313 | { 314 | if ( hHandle != nullptr ) 315 | { 316 | // Let's call a GInput export to get the proper interface 317 | auto ExportFunc = (IGInputPad*(*)())GetProcAddress(hHandle, "GetGInputInterface"); 318 | if ( ExportFunc == nullptr ) 319 | { 320 | ExportFunc = (IGInputPad*(*)())GetProcAddress(hHandle, (LPCSTR)1); 321 | } 322 | 323 | if ( ExportFunc != nullptr ) 324 | { 325 | *pInterfacePtr = pCopiedPtr = ExportFunc(); 326 | if ( pCopiedPtr != nullptr ) 327 | { 328 | HMODULE duplicatedHandle; 329 | if ( GetModuleHandleEx( GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (LPCTSTR)ExportFunc, &duplicatedHandle ) != 0 ) 330 | { 331 | *_GInput_HandlePtr() = duplicatedHandle; 332 | bLoadingResult = true; 333 | return true; 334 | } 335 | } 336 | } 337 | } 338 | } 339 | 340 | *pInterfacePtr = pCopiedPtr = _GInput_SafeMode(); 341 | bLoadingResult = false; 342 | return false; 343 | } 344 | 345 | inline bool GInput_Load_TwoPads(IGInputPad** pInterfacePtr) 346 | { 347 | static IGInputPad* pCopiedPtr[2]; // We keep a backup of the interface pointer in case user calls GInput_Load multiple times 348 | static bool bLoadingResult = false; // Loading result is also cached so GInput_Load always returns the same value when called multiple times 349 | 350 | // Have we attempted to load GInput already? If so, just return a valid interface pointer and return 351 | // The pointer can be either a GInput interface or a dummy, 'safe-mode' interface which got initialised 352 | // due to GInput*.asi loading failure 353 | if ( pCopiedPtr[0] != nullptr && pCopiedPtr[1] != nullptr ) 354 | { 355 | pInterfacePtr[0] = pCopiedPtr[0]; 356 | pInterfacePtr[1] = pCopiedPtr[1]; 357 | return bLoadingResult; 358 | } 359 | 360 | #ifdef GINPUT_COMPILE_CROSSCOMPATIBLE_VERSION 361 | for ( const HMODULE hHandle : ModuleList().GetAllByPrefix( L"GInput" ) ) 362 | #else 363 | const HMODULE hHandle = ModuleList().Get( GINPUT_FILENAMEW ); 364 | #endif 365 | { 366 | if ( hHandle != nullptr ) 367 | { 368 | // Let's call a GInput export to get the proper interface 369 | auto ExportFunc = (IGInputPad**(*)())GetProcAddress(hHandle, "GetGInputInterface_2Pads"); 370 | if ( ExportFunc == nullptr ) 371 | { 372 | ExportFunc = (IGInputPad**(*)())GetProcAddress(hHandle, (LPCSTR)2); 373 | } 374 | 375 | if ( ExportFunc != nullptr ) 376 | { 377 | IGInputPad** pad = ExportFunc(); 378 | pInterfacePtr[0] = pCopiedPtr[0] = pad[0]; 379 | pInterfacePtr[1] = pCopiedPtr[1] = pad[1]; 380 | if ( pCopiedPtr[0] != nullptr && pCopiedPtr[1] != nullptr ) 381 | { 382 | HMODULE duplicatedHandle; 383 | if ( GetModuleHandleEx( GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (LPCTSTR)ExportFunc, &duplicatedHandle ) != 0 ) 384 | { 385 | *_GInput_HandlePtr() = duplicatedHandle; 386 | bLoadingResult = true; 387 | return true; 388 | } 389 | } 390 | } 391 | } 392 | } 393 | 394 | IGInputPad* pad = _GInput_SafeMode(); 395 | pInterfacePtr[0] = pCopiedPtr[0] = pad; 396 | pInterfacePtr[1] = pCopiedPtr[1] = pad; 397 | bLoadingResult = false; 398 | return false; 399 | } 400 | 401 | inline void GInput_Release() 402 | { 403 | HMODULE* pHandle = _GInput_HandlePtr(); 404 | if ( *pHandle != nullptr ) 405 | { 406 | FreeLibrary(*pHandle); 407 | *pHandle = nullptr; 408 | } 409 | } 410 | 411 | #endif -------------------------------------------------------------------------------- /source/LoadingScreen.cpp: -------------------------------------------------------------------------------- 1 | #ifdef GTA3 2 | #include "plugin.h" 3 | #include "LoadingScreen.h" 4 | #include "CTxdStore.h" 5 | #include "CFileMgr.h" 6 | #include "CTimer.h" 7 | #include "Utility.h" 8 | #include "Colors.h" 9 | #include "CScene.h" 10 | #include "CGeneral.h" 11 | #include "CMenuManager.h" 12 | #include "Timer.h" 13 | 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | 22 | #include "dxsdk/d3d9.h" 23 | #pragma comment(lib, "d3d9") 24 | 25 | void CLoadingScreen::Init(bool loaded) { 26 | if (IsActive()) { 27 | return; 28 | } 29 | 30 | if (!loaded) { 31 | LoadSplashes(false, false); 32 | } 33 | 34 | m_currDisplayedSplash = -1; 35 | m_timeSinceLastScreen = GetClockTime(); 36 | m_FadeAlpha = 0; 37 | m_bActive = true; 38 | } 39 | 40 | void CLoadingScreen::Shutdown() { 41 | if (!IsActive()) { 42 | return; 43 | } 44 | 45 | for (auto& splash : m_aSplashes) { 46 | splash.sprite.Delete(); 47 | 48 | int32_t slot = CTxdStore::FindTxdSlot(splash.slot.c_str()); 49 | if (slot != -1) { 50 | CTxdStore::RemoveTxdSlot(slot); 51 | } 52 | } 53 | 54 | m_bActive = false; 55 | } 56 | 57 | void CLoadingScreen::RenderSplash() { 58 | CSprite2d::InitPerFrame(); 59 | CRect rect(-5.0f, -5.0f, SCREEN_WIDTH + 5.0f, SCREEN_HEIGHT + 5.0f); 60 | CRGBA color(255, 255, 255, 255); 61 | RwRenderStateSet(rwRENDERSTATETEXTUREADDRESS, (void*)rwTEXTUREADDRESSCLAMP); 62 | 63 | if (m_bFading) { 64 | GetCurrentDisplayedSplash().Draw(rect, color); 65 | 66 | if (m_currDisplayedSplash > 0) 67 | GetGTALogo().Draw(ScaleX(44.0f - 24.0f), SCREEN_HEIGHT - ScaleY(56.0f + 192.0f), ScaleX(226.0f), ScaleY(226.0f), color); 68 | 69 | if (m_bFadeInNextSplashFromBlack || m_bFadeOutCurrSplashToBlack) { 70 | color.Set(0, 0, 0); 71 | color.a = (m_bFadeInNextSplashFromBlack) ? 255 - m_FadeAlpha : m_FadeAlpha; 72 | 73 | CSprite2d::DrawRect(rect, color); 74 | } 75 | else { 76 | color.a = 255 - m_FadeAlpha; 77 | 78 | m_aSplashes[m_currDisplayedSplash - 1].sprite.Draw(rect, color); 79 | 80 | if (m_currDisplayedSplash - 1 > 0) 81 | GetGTALogo().Draw(ScaleX(44.0f - 24.0f), SCREEN_HEIGHT - ScaleY(56.0f + 192.0f), ScaleX(226.0f), ScaleY(226.0f), color); 82 | } 83 | } 84 | else if (!m_bReadyToDelete) { 85 | GetCurrentDisplayedSplash().Draw(rect, color); 86 | 87 | if (m_currDisplayedSplash > 0) 88 | GetGTALogo().Draw(ScaleX(44.0f - 24.0f), SCREEN_HEIGHT - ScaleY(56.0f + 192.0f), ScaleX(226.0f), ScaleY(226.0f), color); 89 | } 90 | } 91 | 92 | void CLoadingScreen::LoadSplashes(bool starting, bool nvidia) { 93 | int32_t numSplashes = starting ? 1 : (NUM_SPLASHES); 94 | 95 | uint8_t screenIdx[NUM_SPLASHES]; 96 | std::iota(std::begin(screenIdx), std::end(screenIdx), 0); 97 | 98 | LARGE_INTEGER pc; 99 | QueryPerformanceCounter(&pc); 100 | srand(pc.u.LowPart); 101 | 102 | if (!starting) 103 | std::shuffle(std::begin(screenIdx) + 1, std::end(screenIdx) - 1, std::mt19937{ std::random_device{}() }); 104 | 105 | CFileMgr::SetDir("TXD\\"); 106 | 107 | std::fill(m_aSplashes.begin(), m_aSplashes.end(), LoadSc()); 108 | 109 | for (int32_t i = 0; i < numSplashes; i++) { 110 | std::string slotName; 111 | if (starting) { 112 | slotName = nvidia ? "nvidia" : "eax"; 113 | } 114 | else if (i >= numSplashes - 1) { 115 | slotName = "gtalogo"; 116 | } 117 | else { 118 | slotName = std::format("loadsc{}", screenIdx[i]); 119 | } 120 | 121 | std::string txdName = slotName; 122 | txdName += ".txd"; 123 | 124 | int32_t slot = CTxdStore::AddTxdSlot(slotName.c_str()); 125 | CTxdStore::LoadTxd(slot, txdName.c_str()); 126 | CTxdStore::AddRef(slot); 127 | CTxdStore::PushCurrentTxd(); 128 | CTxdStore::SetCurrentTxd(slot); 129 | 130 | m_aSplashes[i].slot = slotName; 131 | m_aSplashes[i].sprite.SetTexture((char*)slotName.c_str()); 132 | 133 | CTxdStore::PopCurrentTxd(); 134 | } 135 | CFileMgr::SetDir(""); 136 | } 137 | 138 | float CLoadingScreen::GetClockTime(bool ignorePauseTime) { 139 | float time = (float)RsTimer() / 1000.0f; 140 | return ignorePauseTime ? time : time - m_PauseTime; 141 | } 142 | 143 | void CLoadingScreen::Continue() { 144 | if (!IsActive()) 145 | return; 146 | 147 | m_bWantToPause = false; 148 | if (m_bPaused) { 149 | m_bPaused = false; 150 | m_PauseTime = GetClockTime() - m_ClockTimeOnPause + m_PauseTime; 151 | } 152 | } 153 | 154 | void CLoadingScreen::RenderLoadingBar() { 155 | if (m_TimeBarAppeared == 0.0f) { 156 | m_TimeBarAppeared = GetClockTime(); 157 | } 158 | 159 | if (m_bLegalScreen || gfLoadingPercentage <= 0.0f || gfLoadingPercentage >= 100.0f) 160 | return; 161 | 162 | DrawProgressBar(ScaleX(44.0f), SCREEN_HEIGHT - ScaleY(56.0f), ScaleX(172.0f), ScaleY(10.0f), gfLoadingPercentage / 100.0f, CRGBA(HUD_COLOUR_RED_LC01, 255), CRGBA(HUD_COLOUR_GREYDARK, 255)); 163 | } 164 | 165 | void CLoadingScreen::StartFading() { 166 | m_bFading = true; 167 | m_FadeAlpha = 0; 168 | m_StartFadeTime = GetClockTime(false); 169 | } 170 | 171 | void CLoadingScreen::DisplayPCScreen() { 172 | if (RwCameraBeginUpdate(Scene.m_pCamera)) { 173 | DefinedState2d(); 174 | RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)(TRUE)); 175 | RenderSplash(); 176 | if (m_currDisplayedSplash > 0 && (!m_bFading || m_currDisplayedSplash != 1)) { 177 | RenderLoadingBar(); 178 | } 179 | RwCameraEndUpdate(Scene.m_pCamera); 180 | RwCameraShowRaster(Scene.m_pCamera, RsGlobal.ps->window, rwRASTERFLIPDONTWAIT); 181 | } 182 | } 183 | 184 | void CLoadingScreen::DisplayPCScreenFix(double targetDuration, uint8_t* alpha, bool direction, float* attenuation) { 185 | auto startTime = std::chrono::high_resolution_clock::now(); 186 | 187 | const double frameDuration = 1.0 / RsGlobal.maxFPS; 188 | 189 | while (true) { 190 | auto currentTime = std::chrono::high_resolution_clock::now(); 191 | auto elapsedTime = std::chrono::duration(currentTime - startTime).count(); 192 | 193 | if (elapsedTime >= targetDuration) 194 | break; 195 | 196 | if (alpha) { 197 | if (!direction) 198 | *alpha = 255 - (uint8_t)((elapsedTime / targetDuration) * 255); 199 | else 200 | *alpha = (uint8_t)((elapsedTime / targetDuration) * 255); 201 | } 202 | 203 | if (attenuation && alpha) { 204 | if (m_bFadeOutCurrSplashToBlack) { 205 | float amplitude = m_bFadeOutCurrSplashToBlack ? (255.0f - *alpha) / 255.0f : 1.0f; 206 | m_loadingVolumeMult = amplitude; 207 | } 208 | } 209 | 210 | DisplayPCScreen(); 211 | 212 | std::this_thread::sleep_for(std::chrono::milliseconds(static_cast(frameDuration * 1000))); 213 | } 214 | } 215 | 216 | void CLoadingScreen::DoPCTitleFadeOut() { 217 | m_bFadeInNextSplashFromBlack = true; 218 | m_currDisplayedSplash = 0; 219 | m_bFading = true; 220 | 221 | m_FadeAlpha = 255; 222 | 223 | DisplayPCScreenFix(SPLASH_DURATION, nullptr, false, nullptr); 224 | 225 | DisplayPCScreenFix(FADE_DURATION, &m_FadeAlpha, false, nullptr); 226 | 227 | m_FadeAlpha = 0; 228 | m_bFadeInNextSplashFromBlack = true; 229 | } 230 | 231 | void CLoadingScreen::DoPCTitleFadeIn() { 232 | m_bFadeInNextSplashFromBlack = true; 233 | m_currDisplayedSplash = 0; 234 | m_bFading = true; 235 | 236 | DisplayPCScreenFix(FADE_DURATION, &m_FadeAlpha, true, nullptr); 237 | 238 | m_FadeAlpha = 255; 239 | 240 | DisplayPCScreen(); 241 | 242 | m_bFading = false; 243 | } 244 | 245 | void CLoadingScreen::DoPCScreenChange(bool finish) { 246 | if (!loadingThread.joinable() && m_loadingVolumeMult > 0.0f) { 247 | loadingThread = std::thread([&]() { 248 | if (RsGlobal.quit) 249 | return; 250 | 251 | if (m_loadSampleToPlay == -1) { 252 | int32_t res = std::uniform_int(0, NUM_LOAD_SAMPLES - 1)(std::mt19937{ std::random_device{}() }); 253 | m_loadSampleToPlay = res; 254 | } 255 | 256 | while (m_loadingVolumeMult > 0.1f) { 257 | CLoadingScreen::sampleManager->AddSampleToQueue((uint8_t)(80 * m_loadingVolumeMult), 0, m_loadSampleToPlay, true, {}, 8, false); 258 | CLoadingScreen::sampleManager->Process(); 259 | } 260 | 261 | sampleManager->StopAllChannels(); 262 | }); 263 | } 264 | 265 | m_bFading = true; 266 | 267 | if (finish) { 268 | m_bFadeOutCurrSplashToBlack = true; 269 | } 270 | else { 271 | m_currDisplayedSplash = std::max((m_currDisplayedSplash + 1) % (std::size(m_aSplashes) - 1), (uint32_t)1); 272 | } 273 | 274 | m_FadeAlpha = 0; 275 | 276 | DisplayPCScreenFix(SPLASH_DURATION, nullptr, false, nullptr); 277 | 278 | DisplayPCScreenFix(FADE_DURATION, &m_FadeAlpha, true, &m_loadingVolumeMult); 279 | 280 | m_FadeAlpha = 255; 281 | DisplayPCScreen(); 282 | 283 | m_bFadeInNextSplashFromBlack = false; 284 | m_bFading = false; 285 | 286 | if (finish) { 287 | m_loadingVolumeMult = 0.0f; 288 | if (loadingThread.joinable()) { 289 | loadingThread.join(); 290 | } 291 | Shutdown(); 292 | } 293 | } 294 | 295 | void CLoadingScreen::NewChunkLoaded() { 296 | if (!IsActive()) 297 | return; 298 | 299 | ++m_numChunksLoaded; 300 | if (m_chunkBarAppeared != -1) { 301 | gfLoadingPercentage = (float)(m_numChunksLoaded - m_chunkBarAppeared) / ((float)NUM_CHUNKS - (float)m_chunkBarAppeared) * 100.0f; 302 | } 303 | 304 | auto now = GetClockTime(); 305 | auto delta = now - m_timeSinceLastScreen; 306 | 307 | if (m_numChunksLoaded == NUM_CHUNKS) { 308 | return DoPCScreenChange(true); 309 | } 310 | 311 | if (m_currDisplayedSplash && delta < SCREEN_CHANGE_TIME) { 312 | DisplayPCScreen(); 313 | } 314 | else { 315 | DoPCScreenChange(false); 316 | m_timeSinceLastScreen = now; 317 | 318 | if (m_chunkBarAppeared == -1) { 319 | m_chunkBarAppeared = m_numChunksLoaded; 320 | } 321 | } 322 | } 323 | #endif 324 | -------------------------------------------------------------------------------- /source/LoadingScreen.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #ifdef GTA3 3 | #include "PluginBase.h" 4 | #include "CSprite2d.h" 5 | #include "Audio.h" 6 | 7 | class CLoadingScreen { 8 | public: 9 | struct LoadSc { 10 | std::string slot; 11 | CSprite2d sprite; 12 | 13 | LoadSc() { 14 | slot.clear(); 15 | sprite = {}; 16 | } 17 | }; 18 | static constexpr bool SKIP_EAX_NVIDIA = true; 19 | 20 | static constexpr float FADE_DURATION = 1.0f; 21 | static constexpr float SPLASH_DURATION = 0.5f; 22 | static constexpr uint32_t NUM_SPLASHES = 21; 23 | static constexpr uint32_t NUM_CHUNKS = 80; 24 | static constexpr float SCREEN_CHANGE_TIME = 4.0f; 25 | static constexpr int32_t NUM_LOAD_SAMPLES = 2; 26 | 27 | static inline std::array m_aSplashes; 28 | 29 | static inline int32_t m_currDisplayedSplash; 30 | static inline int32_t m_numChunksLoaded; 31 | static inline int32_t m_chunkBarAppeared; 32 | 33 | static inline bool m_bActive; 34 | static inline bool m_bWantToPause; 35 | static inline bool m_bPaused; 36 | static inline bool m_bFading; 37 | static inline bool m_bLegalScreen; 38 | static inline bool m_bFadeInNextSplashFromBlack; 39 | static inline bool m_bFadeOutCurrSplashToBlack; 40 | static inline bool m_bReadyToDelete; 41 | 42 | static inline float m_StartFadeTime; 43 | static inline float m_ClockTimeOnPause; 44 | static inline float m_PauseTime; 45 | 46 | static inline float gfLoadingPercentage; 47 | static inline float m_TimeBarAppeared; 48 | 49 | static inline float m_timeSinceLastScreen; 50 | 51 | static inline uint8_t m_FadeAlpha; 52 | 53 | static inline int32_t m_loadSampleToPlay = -1; 54 | static inline float m_loadingVolumeMult = 1.0f; 55 | static inline bool m_loadingDone = false; 56 | static inline std::thread loadingThread = {}; 57 | 58 | static inline std::unique_ptr sampleManager; 59 | 60 | public: 61 | static void Init(bool loaded); 62 | static void Shutdown(); 63 | 64 | static void RenderSplash(); 65 | static void LoadSplashes(bool starting, bool nvidia); 66 | static float GetClockTime(bool ignorePauseTime = true); 67 | static void Continue(); 68 | static void RenderLoadingBar(); 69 | static void StartFading(); 70 | static void DisplayPCScreen(); 71 | static void DoPCTitleFadeOut(); 72 | static void DoPCTitleFadeIn(); 73 | static void DoPCScreenChange(bool finish); 74 | static void NewChunkLoaded(); 75 | static void DisplayPCScreenFix(double targetDuration, uint8_t* alpha, bool direction, float* attenuation); 76 | 77 | static bool IsActive() { 78 | return m_bActive; 79 | } 80 | 81 | static CSprite2d& GetCurrentDisplayedSplash() { 82 | return m_aSplashes[m_currDisplayedSplash].sprite; 83 | } 84 | 85 | static CSprite2d& GetGTALogo() { 86 | return m_aSplashes[NUM_SPLASHES - 1].sprite; 87 | } 88 | 89 | }; 90 | #endif 91 | -------------------------------------------------------------------------------- /source/ModuleList.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | // Stores a list of loaded modules with their names, WITHOUT extension 8 | class ModuleList 9 | { 10 | public: 11 | struct LazyEnumerateTag {}; 12 | 13 | ModuleList() 14 | { 15 | Enumerate(); 16 | } 17 | 18 | explicit ModuleList(LazyEnumerateTag) 19 | { 20 | } 21 | 22 | // Initializes module list 23 | // Needs to be called before any calls to Get or GetAll 24 | void Enumerate() 25 | { 26 | // Cannot enumerate twice without cleaing 27 | assert(m_moduleList.size() == 0); 28 | 29 | constexpr size_t INITIAL_SIZE = sizeof(HMODULE) * 256; 30 | HMODULE* modules = static_cast(malloc(INITIAL_SIZE)); 31 | if (modules != nullptr) 32 | { 33 | typedef BOOL(WINAPI* Func)(HANDLE hProcess, HMODULE* lphModule, DWORD cb, LPDWORD lpcbNeeded); 34 | 35 | HMODULE hLib = LoadLibrary(TEXT("kernel32")); 36 | assert(hLib != nullptr); // If this fails then everything is probably broken anyway 37 | 38 | Func pEnumProcessModules = reinterpret_cast(GetProcAddress(hLib, "K32EnumProcessModules")); 39 | if (pEnumProcessModules == nullptr) 40 | { 41 | // Try psapi 42 | FreeLibrary(hLib); 43 | hLib = LoadLibrary(TEXT("psapi")); 44 | if (hLib != nullptr) 45 | { 46 | pEnumProcessModules = reinterpret_cast(GetProcAddress(hLib, "EnumProcessModules")); 47 | } 48 | } 49 | 50 | if (pEnumProcessModules != nullptr) 51 | { 52 | const HANDLE currentProcess = GetCurrentProcess(); 53 | DWORD cbNeeded = 0; 54 | if (pEnumProcessModules(currentProcess, modules, INITIAL_SIZE, &cbNeeded) != 0) 55 | { 56 | if (cbNeeded > INITIAL_SIZE) 57 | { 58 | HMODULE* newModules = static_cast(realloc(modules, cbNeeded)); 59 | if (newModules != nullptr) 60 | { 61 | modules = newModules; 62 | 63 | if (pEnumProcessModules(currentProcess, modules, cbNeeded, &cbNeeded) != 0) 64 | { 65 | EnumerateInternal(modules, cbNeeded / sizeof(HMODULE)); 66 | } 67 | } 68 | else 69 | { 70 | EnumerateInternal(modules, INITIAL_SIZE / sizeof(HMODULE)); 71 | } 72 | } 73 | else 74 | { 75 | EnumerateInternal(modules, cbNeeded / sizeof(HMODULE)); 76 | } 77 | } 78 | } 79 | 80 | if (hLib != nullptr) 81 | { 82 | FreeLibrary(hLib); 83 | } 84 | 85 | free(modules); 86 | } 87 | } 88 | 89 | // Recreates module list 90 | void ReEnumerate() 91 | { 92 | Clear(); 93 | Enumerate(); 94 | } 95 | 96 | // Clears module list 97 | void Clear() 98 | { 99 | m_moduleList.clear(); 100 | } 101 | 102 | // Gets handle of a loaded module with given name, NULL otherwise 103 | HMODULE Get(const wchar_t* moduleName) const 104 | { 105 | // If vector is empty then we're trying to call it without calling Enumerate first 106 | assert(m_moduleList.size() != 0); 107 | 108 | auto it = std::find_if(m_moduleList.begin(), m_moduleList.end(), [&](const auto& e) { 109 | return _wcsicmp(moduleName, e.second.c_str()) == 0; 110 | }); 111 | return it != m_moduleList.end() ? it->first : nullptr; 112 | } 113 | 114 | // Gets handles to all loaded modules with given name 115 | std::vector GetAll(const wchar_t* moduleName) const 116 | { 117 | // If vector is empty then we're trying to call it without calling Enumerate first 118 | assert(m_moduleList.size() != 0); 119 | 120 | std::vector results; 121 | for (auto& e : m_moduleList) 122 | { 123 | if (_wcsicmp(moduleName, e.second.c_str()) == 0) 124 | { 125 | results.push_back(e.first); 126 | } 127 | } 128 | 129 | return results; 130 | } 131 | 132 | // Gets handle of a loaded module with given prefix, NULL otherwise 133 | HMODULE GetByPrefix(const wchar_t* modulePrefix) const 134 | { 135 | // If vector is empty then we're trying to call it without calling Enumerate first 136 | assert(m_moduleList.size() != 0); 137 | 138 | const size_t len = wcslen(modulePrefix); 139 | auto it = std::find_if(m_moduleList.begin(), m_moduleList.end(), [&](const auto& e) { 140 | return _wcsnicmp(modulePrefix, e.second.c_str(), len) == 0; 141 | }); 142 | return it != m_moduleList.end() ? it->first : nullptr; 143 | } 144 | 145 | // Gets handles to all loaded modules with given prefix 146 | std::vector GetAllByPrefix(const wchar_t* modulePrefix) const 147 | { 148 | // If vector is empty then we're trying to call it without calling Enumerate first 149 | assert(m_moduleList.size() != 0); 150 | 151 | const size_t len = wcslen(modulePrefix); 152 | std::vector results; 153 | for (auto& e : m_moduleList) 154 | { 155 | if (_wcsnicmp(modulePrefix, e.second.c_str(), len) == 0) 156 | { 157 | results.push_back(e.first); 158 | } 159 | } 160 | 161 | return results; 162 | } 163 | 164 | private: 165 | void EnumerateInternal(HMODULE* modules, size_t numModules) 166 | { 167 | size_t moduleNameLength = MAX_PATH; 168 | wchar_t* moduleName = static_cast(malloc(moduleNameLength * sizeof(moduleName[0]))); 169 | if (moduleName != nullptr) 170 | { 171 | m_moduleList.reserve(numModules); 172 | for (size_t i = 0; i < numModules; i++) 173 | { 174 | // Obtain module name, with resizing if necessary 175 | DWORD size; 176 | while (size = GetModuleFileNameW(*modules, moduleName, moduleNameLength), size == moduleNameLength) 177 | { 178 | wchar_t* newName = static_cast(realloc(moduleName, 2 * moduleNameLength * sizeof(moduleName[0]))); 179 | if (newName != nullptr) 180 | { 181 | moduleName = newName; 182 | moduleNameLength *= 2; 183 | } 184 | else 185 | { 186 | size = 0; 187 | break; 188 | } 189 | } 190 | 191 | if (size != 0) 192 | { 193 | const wchar_t* nameBegin = wcsrchr(moduleName, '\\') + 1; 194 | const wchar_t* dotPos = wcsrchr(nameBegin, '.'); 195 | if (dotPos != nullptr) 196 | { 197 | m_moduleList.emplace_back(*modules, std::wstring(nameBegin, dotPos)); 198 | } 199 | else 200 | { 201 | m_moduleList.emplace_back(*modules, nameBegin); 202 | } 203 | } 204 | modules++; 205 | } 206 | 207 | free(moduleName); 208 | } 209 | } 210 | 211 | std::vector< std::pair > m_moduleList; 212 | }; -------------------------------------------------------------------------------- /source/Settings.cpp: -------------------------------------------------------------------------------- 1 | #include "plugin.h" 2 | #include "Settings.h" 3 | 4 | void Settings::Read() { 5 | plugin::config_file config(PLUGIN_PATH("SkyUI.ini")); 6 | 7 | } 8 | -------------------------------------------------------------------------------- /source/Settings.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | class Settings { 4 | public: 5 | 6 | public: 7 | void Read(); 8 | }; 9 | 10 | -------------------------------------------------------------------------------- /source/SkyUIAPI.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "ModuleList.hpp" 3 | 4 | struct ApiCALL { 5 | static inline HMODULE h = nullptr; 6 | 7 | static void* GetFunctionByName(const char* name) { 8 | if (!h) 9 | h = ModuleList().GetByPrefix(L"skyui"); 10 | 11 | if (h) { 12 | auto a = (void* (*)())GetProcAddress(h, name); 13 | 14 | if (a) { 15 | return a; 16 | } 17 | } 18 | return NULL; 19 | } 20 | 21 | template 22 | static void Call(const char* name, Args... args) { 23 | void* f = GetFunctionByName(name); 24 | if (f) 25 | reinterpret_cast(f)(args...); 26 | } 27 | 28 | template 29 | static Ret CallAndReturn(const char* name, Args... args) { 30 | void* f = GetFunctionByName(name); 31 | 32 | if (f) 33 | return reinterpret_cast(f)(args...); 34 | 35 | return NULL; 36 | } 37 | 38 | template 39 | static void CallMethod(const char* name, Args... args) { 40 | void* f = GetFunctionByName(name); 41 | if (f) 42 | reinterpret_cast(f)(args...); 43 | } 44 | 45 | template 46 | static Ret CallMethodAndReturn(const char* name, Args... args) { 47 | void* f = GetFunctionByName(name); 48 | 49 | if (f) 50 | return reinterpret_cast(f)(args...); 51 | 52 | return NULL; 53 | } 54 | }; 55 | 56 | class CMenuManager; 57 | 58 | class SkyUI { 59 | public: 60 | static inline uint32_t GetAlpha(uint32_t a = 255) { 61 | return ApiCALL::CallAndReturn(__FUNCTION__, a); 62 | } 63 | 64 | static inline float GetMenuOffsetX() { 65 | return ApiCALL::CallAndReturn(__FUNCTION__); 66 | } 67 | 68 | static inline bool GetGTA3LCS() { 69 | return ApiCALL::CallAndReturn(__FUNCTION__); 70 | } 71 | 72 | static inline uint8_t GetCurrentInput() { 73 | return ApiCALL::CallAndReturn(__FUNCTION__); 74 | } 75 | 76 | static inline int32_t GetTimeToWaitBeforeStateChange() { 77 | return ApiCALL::CallAndReturn(__FUNCTION__); 78 | } 79 | 80 | static inline uint8_t GetCheckHoverForStandardInput(CMenuManager* _this) { 81 | return ApiCALL::CallAndReturn(__FUNCTION__, _this); 82 | } 83 | 84 | typedef uint8_t(*MenuOptionCB)(uint32_t action, int8_t arrows, bool* back, bool enter); 85 | 86 | #if defined(GTASA) 87 | using char_t = char; 88 | #else 89 | using char_t = wchar_t; 90 | #endif 91 | typedef char_t* (*MenuOptionStringsCB)(uint32_t action); 92 | 93 | static inline void ProcessMenuOptionsCB(MenuOptionCB cb) { 94 | ApiCALL::Call(__FUNCTION__, cb); 95 | } 96 | 97 | static inline void ProcessMenuOptionsStringsCB(MenuOptionStringsCB cb) { 98 | ApiCALL::Call(__FUNCTION__, cb); 99 | } 100 | 101 | static inline void AddEntryToMenuScreen(uint32_t screen, uint32_t entry, uint32_t action, const char* entryName, uint32_t targetScreen, uint32_t orientation) { 102 | ApiCALL::Call(__FUNCTION__, screen, entry, action, entryName, targetScreen, orientation); 103 | } 104 | 105 | static inline void SaveOrLoadSettingsCB(void (*cb)(bool isLoading)) { 106 | ApiCALL::Call(__FUNCTION__, cb); 107 | } 108 | 109 | static inline void SaveSettings() { 110 | ApiCALL::Call(__FUNCTION__); 111 | } 112 | }; 113 | -------------------------------------------------------------------------------- /source/Utility.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "PluginBase.h" 3 | #include "CTimer.h" 4 | #include "extensions/Screen.h" 5 | #include "CSprite2d.h" 6 | #include "CDraw.h" 7 | #include "CText.h" 8 | 9 | #define FLASH_ITEM(on, off) (CTimer::m_snTimeInMilliseconds % on + off < on) 10 | #define FLASH_ITEM_PAUSE_MODE(on, off) (CTimer::m_snTimeInMillisecondsPauseMode % on + off < on) 11 | 12 | #define DEFAULT_SCREEN_WIDTH 640.0f 13 | #define DEFAULT_SCREEN_HEIGHT 480.0f 14 | #define DEFAULT_SCREEN_ASPECT_RATIO (DEFAULT_SCREEN_WIDTH / DEFAULT_SCREEN_HEIGHT) 15 | 16 | static float GetAspectRatio() { 17 | #ifdef GTA3 18 | float& fScreenAspectRatio = *(float*)0x5F53C0; 19 | #elif GTAVC 20 | float& fScreenAspectRatio = *(float*)0x94DD38; 21 | #elif GTASA 22 | float& fScreenAspectRatio = CDraw::ms_fAspectRatio; 23 | #endif 24 | return fScreenAspectRatio; 25 | } 26 | 27 | #define SCREEN_ASPECT_RATIO GetAspectRatio() // (SCREEN_WIDTH / SCREEN_HEIGHT) 28 | 29 | static float ScaleX(float x) { 30 | float f = ((x) * (float)SCREEN_WIDTH / DEFAULT_SCREEN_WIDTH) * DEFAULT_SCREEN_ASPECT_RATIO / SCREEN_ASPECT_RATIO; 31 | return f; 32 | } 33 | 34 | static float ScaleXKeepCentered(float x) { 35 | float f = ((SCREEN_WIDTH == DEFAULT_SCREEN_WIDTH) ? (x) : (SCREEN_WIDTH - ScaleX(DEFAULT_SCREEN_WIDTH)) / 2 + ScaleX((x))); 36 | return f; 37 | } 38 | 39 | static float ScaleY(float y) { 40 | float f = ((y) * (float)SCREEN_HEIGHT / DEFAULT_SCREEN_HEIGHT); 41 | return f; 42 | } 43 | 44 | static float ScaleW(float w) { 45 | float f = ((w) * (float)SCREEN_WIDTH / DEFAULT_SCREEN_WIDTH) * DEFAULT_SCREEN_ASPECT_RATIO / SCREEN_ASPECT_RATIO; 46 | return f; 47 | } 48 | 49 | static float ScaleH(float h) { 50 | float f = ((h) * (float)SCREEN_HEIGHT / DEFAULT_SCREEN_HEIGHT); 51 | return f; 52 | } 53 | 54 | static void Draw2DPolygon(float x1, float y1, float x2, float y2, float x3, float y3, float x4, float y4, const CRGBA& color) { 55 | CSprite2d::SetVertices(x1, y1, x2, y2, x3, y3, x4, y4, color, color, color, color); 56 | RwRenderStateSet(rwRENDERSTATETEXTURERASTER, 0); 57 | RwRenderStateSet(rwRENDERSTATESHADEMODE, (void*)rwSHADEMODEFLAT); 58 | RwRenderStateSet(rwRENDERSTATEZTESTENABLE, (void*)FALSE); 59 | RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void*)FALSE); 60 | RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)(color.a != 255)); 61 | RwIm2DRenderPrimitive(rwPRIMTYPETRIFAN, CSprite2d::maVertices, 4); 62 | RwRenderStateSet(rwRENDERSTATEZTESTENABLE, (void*)TRUE); 63 | RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void*)TRUE); 64 | RwRenderStateSet(rwRENDERSTATESHADEMODE, (void*)rwSHADEMODEGOURAUD); 65 | } 66 | 67 | static void RotateVertices(CVector2D* rect, float x, float y, float angle) { 68 | float xold, yold; 69 | //angle /= 57.2957795; 70 | float _cos = cosf(angle); 71 | float _sin = sinf(angle); 72 | for (unsigned int i = 0; i < 4; i++) { 73 | xold = rect[i].x; 74 | yold = rect[i].y; 75 | rect[i].x = x + (xold - x) * _cos + (yold - y) * _sin; 76 | rect[i].y = y - (xold - x) * _sin + (yold - y) * _cos; 77 | } 78 | } 79 | 80 | static void DrawSpriteWithRotation(CSprite2d* sprite, float x, float y, float w, float h, float angle, CRGBA const& col) { 81 | CVector2D posn[4]; 82 | posn[1].x = x - (w * 0.5f); posn[1].y = y - (h * 0.5f); posn[0].x = x + (w * 0.5f); posn[0].y = y - (h * 0.5f); 83 | posn[3].x = x - (w * 0.5f); posn[3].y = y + (h * 0.5f); posn[2].x = x + (w * 0.5f); posn[2].y = y + (h * 0.5f); 84 | 85 | RotateVertices(posn, x, y, angle); 86 | 87 | if (sprite) 88 | sprite->Draw( 89 | posn[3].x, posn[3].y, posn[2].x, posn[2].y, 90 | posn[1].x, posn[1].y, posn[0].x, posn[0].y, CRGBA(col)); 91 | else 92 | CSprite2d::DrawRect(CRect(x - (w * 0.5f), y - (h * 0.5f), x + (w * 0.5f), y + (h * 0.5f)), col); 93 | } 94 | 95 | static int32_t vertexCount = 0; 96 | 97 | static void Begin() { 98 | vertexCount = 0; 99 | } 100 | 101 | static void SetVertex(float x, float y, float z, float w, float u, float v, int32_t col) { 102 | RwIm2DVertex* maVertices = CSprite2d::maVertices; 103 | maVertices[vertexCount].x = x; 104 | maVertices[vertexCount].y = y; 105 | maVertices[vertexCount].z = 0.0f; 106 | maVertices[vertexCount].rhw = 1.0f / CSprite2d::RecipNearClip; 107 | maVertices[vertexCount].emissiveColor = col; 108 | maVertices[vertexCount].u = u; 109 | maVertices[vertexCount].v = v; 110 | vertexCount++; 111 | } 112 | 113 | static void End(const CSprite2d* sprite) { 114 | if (sprite) 115 | RwRenderStateSet(rwRENDERSTATETEXTURERASTER, sprite->m_pTexture->raster); 116 | else 117 | RwRenderStateSet(rwRENDERSTATETEXTURERASTER, 0); 118 | 119 | RwRenderStateSet(rwRENDERSTATESHADEMODE, (void*)rwSHADEMODEFLAT); 120 | RwRenderStateSet(rwRENDERSTATEZTESTENABLE, (void*)FALSE); 121 | RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void*)FALSE); 122 | RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)TRUE); 123 | RwIm2DRenderPrimitive(rwPRIMTYPETRIFAN, CSprite2d::maVertices, 4); 124 | RwRenderStateSet(rwRENDERSTATEZTESTENABLE, (void*)TRUE); 125 | RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void*)TRUE); 126 | RwRenderStateSet(rwRENDERSTATESHADEMODE, (void*)rwSHADEMODEGOURAUD); 127 | } 128 | 129 | static void DrawTriangle(float x, float y, float scale, float angle, CRGBA const& col) { 130 | CVector2D posn[4]; 131 | float w = scale; 132 | float h = scale; 133 | 134 | posn[1].x = x - (w * 0.5f); posn[1].y = y - (h * 0.5f); posn[0].x = x + (w * 0.5f); posn[0].y = y - (h * 0.5f); 135 | posn[3].x = x; posn[3].y = y + (h * 0.5f); posn[2].x = x; posn[2].y = y + (h * 0.5f); 136 | 137 | RotateVertices(posn, x, y, angle); 138 | Draw2DPolygon(posn[0].x, posn[0].y, posn[1].x, posn[1].y, posn[2].x, posn[2].y, posn[3].x, posn[3].y, CRGBA(col)); 139 | } 140 | 141 | static void DrawUnfilledRect(float x, float y, float thinkness, float w, float h, CRGBA const& col) { 142 | float line = thinkness; 143 | 144 | x -= (w) / 2; 145 | y -= (h) / 2; 146 | CSprite2d::DrawRect(CRect(x, y, x + (w), y + line), col); 147 | CSprite2d::DrawRect(CRect(x + (w), y, x + (w)-line, y + (h)), col); 148 | CSprite2d::DrawRect(CRect(x, y + (h), x + (w), y + (h)-line), col); 149 | CSprite2d::DrawRect(CRect(x, y, x + line, y + (h)), col); 150 | } 151 | 152 | static wchar_t UpperCaseTable[128] = { 153 | 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 154 | 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 155 | 150, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 156 | 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 157 | 149, 173, 173, 175, 176, 177, 178, 179, 180, 181, 182, 158 | 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 159 | 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 160 | 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 161 | 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 162 | 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 163 | 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 164 | 249, 250, 251, 252, 253, 254, 255 165 | }; 166 | 167 | static wchar_t FrenchUpperCaseTable[128] = { 168 | 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 169 | 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 170 | 150, 65, 65, 65, 65, 132, 133, 69, 69, 69, 69, 73, 73, 171 | 73, 73, 79, 79, 79, 79, 85, 85, 85, 85, 173, 173, 175, 172 | 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 173 | 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 174 | 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 175 | 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 176 | 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 177 | 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 178 | 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 179 | 253, 254, 255 180 | }; 181 | 182 | static wchar_t GetUpperCase(wchar_t c) { 183 | if (c >= 'a' && c <= 'z') 184 | return c - 32; 185 | 186 | switch (TheText.encoding) { 187 | case 'e': 188 | break; 189 | case 'f': 190 | if (c >= 128 && c <= 255) 191 | return FrenchUpperCaseTable[c - 128]; 192 | break; 193 | case 'g': 194 | case 'i': 195 | case 's': 196 | if (c >= 128 && c <= 255) 197 | return UpperCaseTable[c - 128]; 198 | break; 199 | default: 200 | break; 201 | } 202 | return c; 203 | } 204 | 205 | static wchar_t GetLowerCase(wchar_t c) { 206 | if (c >= 'A' && c <= 'Z') 207 | return c + 32; 208 | } 209 | 210 | static std::wstring wbuff = {}; 211 | static wchar_t* UpperCase(const wchar_t* s) { 212 | wbuff = s; 213 | for (auto& it : wbuff) 214 | it = GetUpperCase(it); 215 | 216 | return (wchar_t*)wbuff.c_str(); 217 | } 218 | 219 | static wchar_t* LowerCase(wchar_t* s) { 220 | wbuff = s; 221 | for (auto& it : wbuff) 222 | it = GetLowerCase(it); 223 | 224 | return (wchar_t*)wbuff.c_str(); 225 | } 226 | 227 | static RwTexture* CreateRwTexture(int32_t w, int32_t h, uint8_t* p) { 228 | RwTexture* texture = nullptr; 229 | 230 | RwRaster* raster = RwRasterCreate(w, h, 0, rwRASTERTYPETEXTURE | rwRASTERFORMAT8888); 231 | RwUInt32* pixels = (RwUInt32*)RwRasterLock(raster, 0, rwRASTERLOCKWRITE); 232 | 233 | for (int32_t i = 0; i < w * h * 4; i += 4) { 234 | uint8_t r = p[i + 2]; 235 | uint8_t g = p[i + 1]; 236 | uint8_t b = p[i]; 237 | 238 | p[i + 2] = b; 239 | p[i + 1] = g; 240 | p[i] = r; 241 | } 242 | 243 | memcpy(pixels, p, w * h * 4); 244 | RwRasterUnlock(raster); 245 | texture = RwTextureCreate(raster); 246 | RwTextureSetFilterMode(texture, rwFILTERLINEAR); 247 | 248 | return texture; 249 | } 250 | 251 | static void DrawProgressBar(float x, float y, float w, float h, float progress, CRGBA const& front, CRGBA const& back) { 252 | progress = plugin::Clamp(progress, 0.0f, 1.0f); 253 | 254 | float b = ScaleY(2.0f); 255 | float s = ScaleY(4.0f); 256 | CSprite2d::DrawRect(CRect(x - b + s, y - b + s, x + w + b + s, y + h + b + s), CRGBA(0, 0, 0, std::min(back.a, (uint8_t)200))); 257 | 258 | CSprite2d::DrawRect(CRect(x - b, y - b, x + w + b, y + h + b), CRGBA(0, 0, 0, back.a)); 259 | CSprite2d::DrawRect(CRect(x, y, x + w, y + h), back); 260 | 261 | if (progress > 0.0f) 262 | CSprite2d::DrawRect(CRect(x, y, x + w * progress, y + h), front); 263 | } 264 | 265 | static uint64_t RsTimer() { 266 | return plugin::CallAndReturnDyn(0x584890); 267 | } 268 | 269 | static int32_t RsCameraShowRaster(RwCamera* cam) { 270 | return plugin::CallAndReturnDyn(0x5848A0, cam); 271 | } 272 | 273 | static void DefinedState2d() { 274 | RwRenderStateSet(rwRENDERSTATETEXTUREADDRESS, (void*)1); 275 | RwRenderStateSet(rwRENDERSTATETEXTUREPERSPECTIVE, 0); 276 | RwRenderStateSet(rwRENDERSTATEZTESTENABLE, (void*)0); 277 | RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void*)0); 278 | RwRenderStateSet(rwRENDERSTATESHADEMODE, (void*)2); 279 | RwRenderStateSet(rwRENDERSTATETEXTUREFILTER, (void*)2); 280 | RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)0); 281 | RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)5); 282 | RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)6); 283 | RwRenderStateSet(rwRENDERSTATEBORDERCOLOR, (void*)0xFF000000); 284 | RwRenderStateSet(rwRENDERSTATEFOGENABLE, (void*)0); 285 | RwRenderStateSet(rwRENDERSTATECULLMODE, (void*)1); 286 | } 287 | 288 | static int32_t RsCameraBeginUpdate(RwCamera* cam) { 289 | return plugin::CallAndReturnDyn(0x5848B0, cam); 290 | } --------------------------------------------------------------------------------