├── .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 | }
--------------------------------------------------------------------------------