├── .gitignore
├── XLivelessAddon.sln
└── XLivelessAddon
├── XLivelessAddon.vcxproj
├── XLivelessAddon.vcxproj.filters
├── dllmain.cpp
└── includes
├── IniReader
├── .gitattributes
├── IniReader.h
├── LICENSE.txt
├── README.md
└── ini_parser.hpp
├── hooking
├── .gitattributes
├── Hooking.Patterns.cpp
├── Hooking.Patterns.h
├── LICENSE.md
└── README.md
└── injector
├── LICENSE
├── assembly.hpp
├── calling.hpp
├── gvm
├── gvm.hpp
└── translator.hpp
├── hooking.hpp
├── injector.hpp
└── utility.hpp
/.gitignore:
--------------------------------------------------------------------------------
1 | #################
2 | ## Eclipse
3 | #################
4 |
5 | *.pydevproject
6 | .project
7 | .metadata
8 | bin/
9 | tmp/
10 | *.tmp
11 | *.bak
12 | *.swp
13 | *~.nib
14 | local.properties
15 | .classpath
16 | .settings/
17 | .loadpath
18 |
19 | # External tool builders
20 | .externalToolBuilders/
21 |
22 | # Locally stored "Eclipse launch configurations"
23 | *.launch
24 |
25 | # CDT-specific
26 | .cproject
27 |
28 | # PDT-specific
29 | .buildpath
30 |
31 |
32 | #################
33 | ## Visual Studio
34 | #################
35 |
36 | ## Ignore Visual Studio temporary files, build results, and
37 | ## files generated by popular Visual Studio add-ons.
38 |
39 | # User-specific files
40 | *.suo
41 | *.user
42 | *.userosscache
43 | *.sln.docstates
44 |
45 | # User-specific files (MonoDevelop/Xamarin Studio)
46 | *.userprefs
47 |
48 | # Build results
49 | [Dd]ebug/
50 | [Dd]ebugPublic/
51 | [Rr]elease/
52 | [Rr]eleases/
53 | x64/
54 | x86/
55 | bld/
56 | [Bb]in/
57 | [Oo]bj/
58 | [Ll]og/
59 |
60 | # Visual Studio 2015 cache/options directory
61 | .vs/
62 | # Uncomment if you have tasks that create the project's static files in wwwroot
63 | #wwwroot/
64 |
65 | # MSTest test Results
66 | [Tt]est[Rr]esult*/
67 | [Bb]uild[Ll]og.*
68 |
69 | # NUNIT
70 | *.VisualState.xml
71 | TestResult.xml
72 |
73 | # Build Results of an ATL Project
74 | [Dd]ebugPS/
75 | [Rr]eleasePS/
76 | dlldata.c
77 |
78 | # DNX
79 | project.lock.json
80 | artifacts/
81 |
82 | *_i.c
83 | *_p.c
84 | *_i.h
85 | *.ilk
86 | *.meta
87 | *.obj
88 | *.pch
89 | *.pdb
90 | *.pgc
91 | *.pgd
92 | *.rsp
93 | *.sbr
94 | *.tlb
95 | *.tli
96 | *.tlh
97 | *.tmp
98 | *.tmp_proj
99 | *.log
100 | *.vspscc
101 | *.vssscc
102 | .builds
103 | *.pidb
104 | *.svclog
105 | *.scc
106 |
107 | # Chutzpah Test files
108 | _Chutzpah*
109 |
110 | # Visual C++ cache files
111 | ipch/
112 | *.aps
113 | *.ncb
114 | *.opendb
115 | *.opensdf
116 | *.sdf
117 | *.cachefile
118 | *.db
119 |
120 | # Visual Studio profiler
121 | *.psess
122 | *.vsp
123 | *.vspx
124 | *.sap
125 |
126 | # TFS 2012 Local Workspace
127 | $tf/
128 |
129 | # Guidance Automation Toolkit
130 | *.gpState
131 |
132 | # ReSharper is a .NET coding add-in
133 | _ReSharper*/
134 | *.[Rr]e[Ss]harper
135 | *.DotSettings.user
136 |
137 | # JustCode is a .NET coding add-in
138 | .JustCode
139 |
140 | # TeamCity is a build add-in
141 | _TeamCity*
142 |
143 | # DotCover is a Code Coverage Tool
144 | *.dotCover
145 |
146 | # NCrunch
147 | _NCrunch_*
148 | .*crunch*.local.xml
149 | nCrunchTemp_*
150 |
151 | # MightyMoose
152 | *.mm.*
153 | AutoTest.Net/
154 |
155 | # Web workbench (sass)
156 | .sass-cache/
157 |
158 | # Installshield output folder
159 | [Ee]xpress/
160 |
161 | # DocProject is a documentation generator add-in
162 | DocProject/buildhelp/
163 | DocProject/Help/*.HxT
164 | DocProject/Help/*.HxC
165 | DocProject/Help/*.hhc
166 | DocProject/Help/*.hhk
167 | DocProject/Help/*.hhp
168 | DocProject/Help/Html2
169 | DocProject/Help/html
170 |
171 | # Click-Once directory
172 | publish/
173 |
174 | # Publish Web Output
175 | *.[Pp]ublish.xml
176 | *.azurePubxml
177 | # TODO: Comment the next line if you want to checkin your web deploy settings
178 | # but database connection strings (with potential passwords) will be unencrypted
179 | *.pubxml
180 | *.publishproj
181 |
182 | # NuGet Packages
183 | *.nupkg
184 | # The packages folder can be ignored because of Package Restore
185 | **/packages/*
186 | # except build/, which is used as an MSBuild target.
187 | !**/packages/build/
188 | # Uncomment if necessary however generally it will be regenerated when needed
189 | #!**/packages/repositories.config
190 | # NuGet v3's project.json files produces more ignoreable files
191 | *.nuget.props
192 | *.nuget.targets
193 |
194 | # Microsoft Azure Build Output
195 | csx/
196 | *.build.csdef
197 |
198 | # Microsoft Azure Emulator
199 | ecf/
200 | rcf/
201 |
202 | # Microsoft Azure ApplicationInsights config file
203 | ApplicationInsights.config
204 |
205 | # Windows Store app package directories and files
206 | AppPackages/
207 | BundleArtifacts/
208 | Package.StoreAssociation.xml
209 | _pkginfo.txt
210 |
211 | # Visual Studio cache files
212 | # files ending in .cache can be ignored
213 | *.[Cc]ache
214 | # but keep track of directories ending in .cache
215 | !*.[Cc]ache/
216 |
217 | # Others
218 | ClientBin/
219 | ~$*
220 | *~
221 | *.dbmdl
222 | *.dbproj.schemaview
223 | *.pfx
224 | *.publishsettings
225 | node_modules/
226 | orleans.codegen.cs
227 |
228 | # RIA/Silverlight projects
229 | Generated_Code/
230 |
231 | # Backup & report files from converting an old project file
232 | # to a newer Visual Studio version. Backup files are not needed,
233 | # because we have git ;-)
234 | _UpgradeReport_Files/
235 | Backup*/
236 | UpgradeLog*.XML
237 | UpgradeLog*.htm
238 |
239 | # SQL Server files
240 | *.mdf
241 | *.ldf
242 |
243 | # Business Intelligence projects
244 | *.rdl.data
245 | *.bim.layout
246 | *.bim_*.settings
247 |
248 | # Microsoft Fakes
249 | FakesAssemblies/
250 |
251 | # GhostDoc plugin setting file
252 | *.GhostDoc.xml
253 |
254 | # Node.js Tools for Visual Studio
255 | .ntvs_analysis.dat
256 |
257 | # Visual Studio 6 build log
258 | *.plg
259 |
260 | # Visual Studio 6 workspace options file
261 | *.opt
262 |
263 | # Visual Studio LightSwitch build output
264 | **/*.HTMLClient/GeneratedArtifacts
265 | **/*.DesktopClient/GeneratedArtifacts
266 | **/*.DesktopClient/ModelManifest.xml
267 | **/*.Server/GeneratedArtifacts
268 | **/*.Server/ModelManifest.xml
269 | _Pvt_Extensions
270 |
271 | # Paket dependency manager
272 | .paket/paket.exe
273 |
274 | # FAKE - F# Make
275 | .fake/
276 |
277 | # JetBrains Rider
278 | .idea/
279 | *.sln.iml
280 |
281 | #############
282 | ## Windows detritus
283 | #############
284 |
285 | # Windows image file caches
286 | Thumbs.db
287 | ehthumbs.db
288 |
289 | # Folder config file
290 | Desktop.ini
291 |
292 | # Recycle Bin used on file shares
293 | $RECYCLE.BIN/
294 |
295 | # Mac crap
296 | .DS_Store
297 |
298 |
299 | #############
300 | ## Python
301 | #############
302 |
303 | *.py[co]
304 |
305 | # Packages
306 | *.egg
307 | *.egg-info
308 | dist/
309 | build/
310 | eggs/
311 | parts/
312 | var/
313 | sdist/
314 | develop-eggs/
315 | .installed.cfg
316 |
317 | # Installer logs
318 | pip-log.txt
319 |
320 | # Unit test / coverage reports
321 | .coverage
322 | .tox
323 |
324 | #Translations
325 | *.mo
326 |
327 | #Mr Developer
328 | .mr.developer.cfg
329 |
330 | #Executables and IDA files
331 | *.exe
332 | *.idb
--------------------------------------------------------------------------------
/XLivelessAddon.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio 14
4 | VisualStudioVersion = 14.0.25420.1
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "XLivelessAddon", "XLivelessAddon\XLivelessAddon.vcxproj", "{EEFEDC38-B2ED-4E32-B351-087EBE23C8F3}"
7 | EndProject
8 | Global
9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
10 | Debug|x64 = Debug|x64
11 | Debug|x86 = Debug|x86
12 | Release|x64 = Release|x64
13 | Release|x86 = Release|x86
14 | EndGlobalSection
15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
16 | {EEFEDC38-B2ED-4E32-B351-087EBE23C8F3}.Debug|x64.ActiveCfg = Debug|x64
17 | {EEFEDC38-B2ED-4E32-B351-087EBE23C8F3}.Debug|x64.Build.0 = Debug|x64
18 | {EEFEDC38-B2ED-4E32-B351-087EBE23C8F3}.Debug|x86.ActiveCfg = Debug|Win32
19 | {EEFEDC38-B2ED-4E32-B351-087EBE23C8F3}.Debug|x86.Build.0 = Debug|Win32
20 | {EEFEDC38-B2ED-4E32-B351-087EBE23C8F3}.Release|x64.ActiveCfg = Release|x64
21 | {EEFEDC38-B2ED-4E32-B351-087EBE23C8F3}.Release|x64.Build.0 = Release|x64
22 | {EEFEDC38-B2ED-4E32-B351-087EBE23C8F3}.Release|x86.ActiveCfg = Release|Win32
23 | {EEFEDC38-B2ED-4E32-B351-087EBE23C8F3}.Release|x86.Build.0 = Release|Win32
24 | EndGlobalSection
25 | GlobalSection(SolutionProperties) = preSolution
26 | HideSolutionNode = FALSE
27 | EndGlobalSection
28 | EndGlobal
29 |
--------------------------------------------------------------------------------
/XLivelessAddon/XLivelessAddon.vcxproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | Win32
7 |
8 |
9 | Release
10 | Win32
11 |
12 |
13 | Debug
14 | x64
15 |
16 |
17 | Release
18 | x64
19 |
20 |
21 |
22 | {EEFEDC38-B2ED-4E32-B351-087EBE23C8F3}
23 | Win32Proj
24 | XLivelessAddon
25 | 10.0
26 |
27 |
28 |
29 | DynamicLibrary
30 | true
31 | v142
32 | MultiByte
33 |
34 |
35 | DynamicLibrary
36 | false
37 | v142
38 | true
39 | MultiByte
40 |
41 |
42 | DynamicLibrary
43 | true
44 | v142
45 | Unicode
46 |
47 |
48 | DynamicLibrary
49 | false
50 | v142
51 | true
52 | Unicode
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 | true
74 | .asi
75 | $(Configuration)\
76 |
77 |
78 | true
79 |
80 |
81 | false
82 | .asi
83 | $(Configuration)\
84 | $(SolutionDir)$(Configuration)\plugins\
85 |
86 |
87 | false
88 |
89 |
90 |
91 |
92 |
93 | Level3
94 | Disabled
95 | WIN32;_DEBUG;_WINDOWS;_USRDLL;XLIVELESSADDON_EXPORTS;%(PreprocessorDefinitions)
96 | /std:c++latest %(AdditionalOptions)
97 |
98 |
99 | Windows
100 | true
101 |
102 |
103 | call "$(SolutionDir)Debug\copy.bat"
104 |
105 |
106 |
107 |
108 |
109 |
110 | Level3
111 | Disabled
112 | _DEBUG;_WINDOWS;_USRDLL;XLIVELESSADDON_EXPORTS;%(PreprocessorDefinitions)
113 |
114 |
115 | Windows
116 | true
117 |
118 |
119 |
120 |
121 | Level3
122 |
123 |
124 | MaxSpeed
125 | true
126 | true
127 | WIN32;NDEBUG;_WINDOWS;_USRDLL;XLIVELESSADDON_EXPORTS;%(PreprocessorDefinitions)
128 | MultiThreaded
129 | /std:c++latest %(AdditionalOptions)
130 |
131 |
132 | Windows
133 | true
134 | true
135 | true
136 |
137 |
138 |
139 |
140 | Level3
141 |
142 |
143 | MaxSpeed
144 | true
145 | true
146 | NDEBUG;_WINDOWS;_USRDLL;XLIVELESSADDON_EXPORTS;%(PreprocessorDefinitions)
147 |
148 |
149 | Windows
150 | true
151 | true
152 | true
153 |
154 |
155 |
156 |
157 |
158 |
159 |
160 |
161 |
162 |
--------------------------------------------------------------------------------
/XLivelessAddon/XLivelessAddon.vcxproj.filters:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF}
6 | cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx
7 |
8 |
9 | {93995380-89BD-4b04-88EB-625FBE52EBFB}
10 | h;hh;hpp;hxx;hm;inl;inc;xsd
11 |
12 |
13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01}
14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms
15 |
16 |
17 |
18 |
19 | Source Files
20 |
21 |
22 | Source Files
23 |
24 |
25 |
--------------------------------------------------------------------------------
/XLivelessAddon/dllmain.cpp:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include ".\includes\IniReader\IniReader.h"
4 | #include ".\includes\injector\injector.hpp"
5 | #include ".\includes\injector\calling.hpp"
6 | #include ".\includes\injector\hooking.hpp"
7 | #include ".\includes\injector\assembly.hpp"
8 | #include ".\includes\injector\utility.hpp"
9 | #include ".\includes\hooking\Hooking.Patterns.h"
10 |
11 | bool bDelay;
12 | char* pszPath;
13 | std::string szCustomSavePath;
14 | std::string szCustomSettingsPath;
15 |
16 | #define WM_ROM (43858)
17 |
18 | const static uint8_t staticData[] = { 0x08, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, 0xF1,
19 | 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xF0, 0x00,
20 | 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00,
21 | 0x00, 0x0C, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
22 | 0xF3, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x06,
23 | 0x00, 0x00, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x05, 0x00,
24 | 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xF2, 0x00, 0x00,
25 | 0x00
26 | };
27 |
28 | struct dataStruct
29 | {
30 | char a[16];
31 | int action;
32 | char* act100Addr;
33 | char* act18Addr;
34 | };
35 |
36 | // change savefile path to "%USERPROFILE%\Documents\Rockstar Games\GTA IV\savegames\"
37 | void getSavefilePath(int __unused, char * pBuffer, char * pszSaveName)
38 | {
39 | if (szCustomSavePath.empty())
40 | {
41 | strcpy_s(pBuffer, 256, pszPath);
42 | strcat_s(pBuffer, 256, "savegames");
43 | }
44 | else
45 | {
46 | szCustomSavePath.copy(pBuffer, 256);
47 | pBuffer[szCustomSavePath.length()] = '\0';
48 | }
49 |
50 | // check path and create directory if necessary
51 | DWORD attrs = GetFileAttributes(pBuffer);
52 | if (attrs == INVALID_FILE_ATTRIBUTES)
53 | CreateDirectory(pBuffer, NULL);
54 | else if (!(attrs & FILE_ATTRIBUTE_DIRECTORY))
55 | {
56 | strcpy_s(pBuffer, 256, pszSaveName);
57 | return;
58 | }
59 |
60 | if (pszSaveName)
61 | {
62 | strcat_s(pBuffer, 256, "\\");
63 | strcat_s(pBuffer, 256, pszSaveName);
64 | }
65 | }
66 |
67 | HANDLE CreateFileHook(_In_ LPCSTR lpFileName, _In_ DWORD dwDesiredAccess, _In_ DWORD dwShareMode, _In_opt_ LPSECURITY_ATTRIBUTES lpSecurityAttributes, _In_ DWORD dwCreationDisposition, _In_ DWORD dwFlagsAndAttributes, _In_opt_ HANDLE hTemplateFile)
68 | {
69 | return CreateFileA(lpFileName, dwDesiredAccess, dwShareMode, lpSecurityAttributes, dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile);
70 | }
71 |
72 | BOOL CloseHandleHook(_In_ HANDLE hObject)
73 | {
74 | __try
75 | {
76 | return CloseHandle(hObject);
77 | }
78 | __except (EXCEPTION_CONTINUE_EXECUTION)
79 | {}
80 |
81 | return TRUE;
82 | }
83 |
84 | BOOL GetFileSizeExHook(HANDLE hFile, PLARGE_INTEGER lpFileSize)
85 | {
86 | return GetFileSizeEx(hFile, lpFileSize);
87 | }
88 |
89 | BOOL ReadFileHook(HANDLE hFile, LPVOID pBuffer, DWORD nNumberOfBytesRead, LPDWORD lpNumberOfBytesRead, LPOVERLAPPED lpOverlapped)
90 | {
91 | return ReadFile(hFile, pBuffer, nNumberOfBytesRead, lpNumberOfBytesRead, lpOverlapped);
92 | }
93 |
94 | DWORD SetFilePointerHook(HANDLE hFile, LONG dtm, PLONG dtmHigh, DWORD mm)
95 | {
96 | return SetFilePointer(hFile, dtm, dtmHigh, mm);
97 | }
98 |
99 | static LRESULT WINAPI WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
100 | {
101 | if (uMsg == WM_ROM)
102 | {
103 | dataStruct* str = (dataStruct*)lParam;
104 |
105 | switch (str->action)
106 | {
107 | case 100:
108 | *(DWORD*)(str->act100Addr) = 1;
109 | break;
110 | case 18:
111 | memcpy(str->act18Addr, staticData, sizeof(staticData));
112 | break;
113 | case 51:
114 | return (LRESULT)(str->act100Addr + (DWORD)str->act18Addr);
115 | }
116 |
117 | return 0;
118 | }
119 |
120 | return 1;
121 | }
122 |
123 | static HWND CreateROMWindow()
124 | {
125 | WNDCLASSEXW wc;
126 | memset(&wc, 0, sizeof(wc));
127 | wc.cbSize = sizeof(wc);
128 |
129 | wc.style = 3;
130 | wc.lpfnWndProc = WndProc;
131 | wc.lpszClassName = L"banana";
132 | wc.hInstance = GetModuleHandle(NULL);
133 | wc.hbrBackground = (HBRUSH)5;
134 | wc.hCursor = LoadCursor(0, MAKEINTRESOURCE(0x7F00));
135 |
136 | RegisterClassExW(&wc);
137 |
138 | HWND hWnd = CreateWindowExW(0, L"banana", L"", 0xCC00000, 0, 0, 0, 0, 0, 0, GetModuleHandle(NULL), 0);
139 |
140 | return hWnd;
141 | }
142 |
143 | void __stdcall SendMessageFakie(int, int, int, int)
144 | {
145 |
146 | }
147 |
148 | int __cdecl dfaInit(int, int, int)
149 | {
150 | return 1;
151 | }
152 |
153 | injector::hook_back hbsub_7870A0;
154 | void __cdecl sub_7870A0(int a1)
155 | {
156 | static bool bOnce = false;
157 | if (!bOnce)
158 | {
159 | if (a1 == 0)
160 | {
161 | bool bNoLoad = (GetAsyncKeyState(VK_SHIFT) & 0xF000) != 0;
162 | if (!bNoLoad)
163 | a1 = 6;
164 |
165 | bOnce = true;
166 | }
167 | }
168 | return hbsub_7870A0.fun(a1);
169 | }
170 |
171 | injector::hook_back hbGetVidMem;
172 | int32_t GetVidMem()
173 | {
174 | auto v = hbGetVidMem.fun();
175 | return v <= 0 ? INT_MAX : v;
176 | }
177 |
178 | LRESULT WINAPI DefWindowProcAProxy(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam)
179 | {
180 | static bool bOnce = false;
181 | if (!bOnce)
182 | {
183 | SetWindowLong(hWnd, GWL_STYLE, GetWindowLong(hWnd, GWL_STYLE) & ~WS_OVERLAPPEDWINDOW);
184 | bOnce = true;
185 | }
186 |
187 | return DefWindowProcA(hWnd, Msg, wParam, lParam);
188 | }
189 |
190 | LRESULT WINAPI DefWindowProcWProxy(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam)
191 | {
192 | static bool bOnce = false;
193 | if (!bOnce)
194 | {
195 | SetWindowLong(hWnd, GWL_STYLE, GetWindowLong(hWnd, GWL_STYLE) & ~WS_OVERLAPPEDWINDOW);
196 | bOnce = true;
197 | }
198 |
199 | return DefWindowProcW(hWnd, Msg, wParam, lParam);
200 | }
201 |
202 | HKEY Key;
203 | std::string SubKey;
204 | LSTATUS WINAPI CustomRegOpenKeyA(HKEY hKey, LPCSTR lpSubKey, PHKEY phkResult)
205 | {
206 | if (strstr(lpSubKey, "Rockstar Games") != NULL)
207 | {
208 | Key = hKey;
209 | SubKey = lpSubKey;
210 | *phkResult = NULL;
211 | return ERROR_SUCCESS;
212 | }
213 | else
214 | return ::RegOpenKeyA(hKey, lpSubKey, phkResult);
215 | }
216 |
217 | LSTATUS WINAPI CustomRegOpenKeyExA(HKEY hKey, LPCSTR lpSubKey, DWORD ulOptions, REGSAM samDesired, PHKEY phkResult)
218 | {
219 | if (strstr(lpSubKey, "Rockstar Games") != NULL)
220 | {
221 | Key = hKey;
222 | SubKey = lpSubKey;
223 | *phkResult = NULL;
224 | return ERROR_SUCCESS;
225 | }
226 | else
227 | return ::RegOpenKeyExA(hKey, lpSubKey, ulOptions, samDesired, phkResult);
228 | }
229 |
230 | LSTATUS WINAPI CustomRegQueryValueExA(HKEY hKey, LPCSTR lpValueName, LPDWORD lpReserved, LPDWORD lpType, LPBYTE lpData, LPDWORD lpcbData)
231 | {
232 | if (hKey == NULL)
233 | {
234 | if (strstr(lpValueName, "InstallFolder") != NULL)
235 | {
236 | char path[MAX_PATH];
237 | GetModuleFileName(GetModuleHandle(NULL), path, MAX_PATH);
238 | auto ptr = strrchr(path, '\\');
239 | *(ptr + 1) = '\0';
240 | auto szPath = std::string_view(path);
241 |
242 | if (lpData != NULL)
243 | {
244 | std::string_view str((char*)lpData, *lpcbData);
245 | *lpcbData = min(szPath.length(), str.length());
246 | szPath.copy((char*)str.data(), *lpcbData, 0);
247 | lpData[*lpcbData] = '\0';
248 | }
249 | else
250 | {
251 | *lpcbData = szPath.length();
252 | }
253 | }
254 | else
255 | {
256 | ::RegOpenKeyExA(Key, SubKey.c_str(), 0, KEY_READ, &hKey);
257 | return ::RegQueryValueExA(hKey, lpValueName, lpReserved, lpType, lpData, lpcbData);
258 | }
259 | return ERROR_SUCCESS;
260 | }
261 | else
262 | return ::RegQueryValueExA(hKey, lpValueName, lpReserved, lpType, lpData, lpcbData);
263 | }
264 |
265 | DWORD WINAPI Init(LPVOID)
266 | {
267 | if(GetConsoleWindow())
268 | FreeConsole();
269 |
270 | auto pattern = hook::pattern("B0 04 B2 18 B1 20");
271 | if (!(pattern.size() > 0) && !bDelay)
272 | {
273 | bDelay = true;
274 | CreateThread(0, 0, (LPTHREAD_START_ROUTINE)&Init, NULL, 0, NULL);
275 | return 0;
276 | }
277 |
278 | if (bDelay)
279 | {
280 | while (!(pattern.size() > 0))
281 | pattern = hook::pattern("B0 04 B2 18 B1 20");
282 | }
283 |
284 | CIniReader iniReader("");
285 | szCustomSavePath = iniReader.ReadString("MAIN", "CustomSavePath", "");
286 | szCustomSettingsPath = iniReader.ReadString("MAIN", "CustomSettingsPath", "");
287 | bool bRemoveRegistryPathDependency = iniReader.ReadInteger("MAIN", "RemoveRegistryPathDependency", 1) != 0;
288 | bool bSkipWebConnect = iniReader.ReadInteger("MAIN", "SkipWebConnect", 0) != 0;
289 | bool bSkipIntro = iniReader.ReadInteger("MAIN", "SkipIntro", 0) != 0;
290 | bool bSkipMenu = iniReader.ReadInteger("MAIN", "SkipMenu", 0) != 0;
291 | bool bDoNotPauseOnMinimize = iniReader.ReadInteger("MAIN", "DoNotPauseOnMinimize", 0) != 0;
292 | bool bBorderlessWindowed = iniReader.ReadInteger("MAIN", "BorderlessWindowed", 0) != 0;
293 | bool bVRAMFix = iniReader.ReadInteger("MAIN", "VRAMFix", 1) != 0;
294 |
295 | // Unprotect image - make .text and .rdata section writeable
296 | // get load address of the exe
297 | DWORD dwLoadOffset = (DWORD)GetModuleHandle(NULL);
298 | BYTE * pImageBase = reinterpret_cast(dwLoadOffset);
299 | PIMAGE_DOS_HEADER pDosHeader = reinterpret_cast (dwLoadOffset);
300 | PIMAGE_NT_HEADERS pNtHeader = reinterpret_cast (pImageBase + pDosHeader->e_lfanew);
301 | PIMAGE_SECTION_HEADER pSection = IMAGE_FIRST_SECTION(pNtHeader);
302 |
303 | for (int iSection = 0; iSection < pNtHeader->FileHeader.NumberOfSections; ++iSection, ++pSection)
304 | {
305 | char * pszSectionName = reinterpret_cast(pSection->Name);
306 | if (!strcmp(pszSectionName, ".text") || !strcmp(pszSectionName, ".rdata"))
307 | {
308 | DWORD dwPhysSize = (pSection->Misc.VirtualSize + 4095) & ~4095;
309 | DWORD oldProtect;
310 | DWORD newProtect = (pSection->Characteristics & IMAGE_SCN_MEM_EXECUTE) ? PAGE_EXECUTE_READWRITE : PAGE_READWRITE;
311 | if (!VirtualProtect(reinterpret_cast (dwLoadOffset + pSection->VirtualAddress), dwPhysSize, newProtect, &oldProtect))
312 | {
313 | ExitProcess(0);
314 | }
315 | }
316 | }
317 |
318 | //static bool isEFLC = false;
319 | //pattern = hook::pattern("68 ? ? ? ? E8 ? ? ? ? 8B F0 83 C4 ? 85 F6 0F 84 ? ? ? ? 6A 04");
320 | //if (pattern.size() > 0)
321 | //{
322 | // if (strstr(*(char**)pattern.get(0).get(1), "EFLC") != NULL)
323 | // {
324 | // isEFLC = true;
325 | // }
326 | //}
327 |
328 | auto gv = [&dwLoadOffset]() -> uint32_t
329 | {
330 | DWORD signature = *(DWORD*)(0x608C34 + dwLoadOffset - 0x400000);
331 | if (signature == 0xC25DE58B)
332 | return 1000;
333 | else if (signature == 0x831F7518)
334 | return 1010;
335 | else if (signature == 0xC483FFE4)
336 | return 1020;
337 | else if (signature == 0x280F0000)
338 | return 1030;
339 | else if (signature == 0x110FF300)
340 | return 1040;
341 | else if (signature == 0xF3385058)
342 | return 1050;
343 | else if (signature == 0x00A42494)
344 | return 1060;
345 | else if (signature == 0x1006E857)
346 | return 1070;
347 | else if (signature == 0x404B100F)
348 | return 1080;
349 | else if (signature == 0x5C0FF301)
350 | return 1100;
351 | else if (signature == 0x0F14247C)
352 | return 1110;
353 | else if (signature == 0x0D5C0FF3)
354 | return 1120;
355 | else if (signature == 0x04C1F600)
356 | return 1130;
357 | else
358 | return UINT_MAX;
359 | };
360 |
361 | auto game_version = gv();
362 | auto gv_not = [&game_version](std::initializer_list v) -> bool
363 | {
364 | if (std::find(std::begin(v), std::end(v), game_version) != std::end(v))
365 | return false;
366 |
367 | return true;
368 | };
369 |
370 | // savegames [v1000 - v1008]
371 | {
372 | pattern = hook::pattern("8A 10 83 C0 01 84 D2 75 F7 56 57 BF ? ? ? ? 2B C1 8B F1 83 C7 FF 8A 4F 01 83 C7 01");
373 | if (pattern.count(3).size() > 0)
374 | {
375 | auto range_end = (uintptr_t)pattern.get(2).get(0);
376 | auto tmp = hook::pattern(range_end - 0x80, range_end, "8A 88 ? ? ? ? 88 88 ? ? ? ? 83 C0 01");
377 | pszPath = *tmp.get(0).get(2);
378 |
379 | pattern = hook::pattern("75 ? 83 C1 01 83 C0 01 83 F9 04");
380 | if (pattern.size() > 0)
381 | injector::MakeNOP(pattern.get(0).get(0), 2, true); // NOP - save file CRC32 check
382 |
383 | pattern = hook::pattern("55 8B EC 83 E4 F8 83 EC 4C 56");
384 | if (pattern.size() > 0)
385 | injector::MakeJMP(pattern.get(0).get(0), getSavefilePath, true); // replace getSavefilePath
386 | }
387 | }
388 |
389 | //settings [v1001-v1008]
390 | if (!szCustomSettingsPath.empty())
391 | {
392 | pattern = hook::pattern("68 ? ? ? ? B9 ? ? ? ? E8 ? ? ? ? 8D 4C 24 04 E8 ? ? ? ? 5B 83 C4 08 C3");
393 | if (pattern.size() > 0)
394 | {
395 | static auto buf = *pattern.count(4).get(1).get(1);
396 | static auto unk_1003C10 = *pattern.count(4).get(1).get(6);
397 | struct SettingsPath
398 | {
399 | void operator()(injector::reg_pack& regs)
400 | {
401 | std::string s;
402 | if (szCustomSettingsPath.find(":") == std::string::npos)
403 | {
404 | char path[MAX_PATH];
405 | GetModuleFileName(GetModuleHandle(NULL), path, MAX_PATH);
406 | auto ptr = strrchr(path, '\\');
407 | *(ptr + 1) = '\0';
408 | s += path;
409 | }
410 |
411 | if (s.back() != '\\')
412 | s += "\\";
413 |
414 | s += szCustomSettingsPath + "\\" + (char*)regs.ebx + "\\";
415 |
416 | if (GetFileAttributes(s.c_str()) == INVALID_FILE_ATTRIBUTES)
417 | CreateDirectory(s.c_str(), NULL);
418 |
419 | if (GetFileAttributes(s.c_str()) & FILE_ATTRIBUTE_DIRECTORY)
420 | {
421 | s.copy(buf, 256);
422 | buf[s.length()] = '\0';
423 | }
424 |
425 | regs.ecx = unk_1003C10;
426 | }
427 | };
428 | injector::MakeInline(pattern.count(4).get(1).get(5));
429 | injector::MakeInline(pattern.count(4).get(2).get(5));
430 | }
431 | }
432 |
433 | // process patches
434 | uintptr_t* jmp_efc = nullptr;
435 | pattern = hook::pattern("68 ? ? ? ? 89 35 ? ? ? ? E8 ? ? ? ? 83 C4 04 84 C0 0F 84");
436 | if (pattern.size() > 0)
437 | jmp_efc = pattern.get(0).get(0);
438 |
439 | // disable sleep [v1002 - v1008]
440 | pattern = hook::pattern("68 88 13 00 00 FF 15");
441 | if (pattern.size() > 0)
442 | injector::WriteMemory(pattern.get(0).get(1), 1, true);
443 |
444 | // RETN - enable debugger in error menu (don't load WER.dll) [v1000 - v1008]
445 | pattern = hook::pattern("81 EC ? ? ? ? A1 ? ? ? ? 33 C4 89 84 24 ? ? 00 00 53 56 8B 35 ? ? ? ? 68");
446 | if (pattern.size() > 0)
447 | injector::WriteMemory(pattern.get(0).get(0), 0xC3, true);
448 |
449 | // RETN 8 - certificates check [v1002 - v1008]
450 | pattern = hook::pattern("81 EC ? ? ? ? A1 ? ? ? ? 33 C4 89 84 24 ? ? ? ? 53 8B 9C 24 ? ? ? ? 55 8B AC ? ? ? ? ? 8B 45 00");
451 | if (pattern.size() > 0)
452 | injector::MakeRET(pattern.get(0).get(0), 8, true);
453 |
454 | // RETN - skip files.txt hash check [v1002 - v1005]
455 | pattern = hook::pattern("81 EC ? ? ? ? A1 ? ? ? ? 33 C4 89 84 24 ? ? ? ? 83 3D ? ? ? ? ? 56 57 0F 85 ? ? ? ? 83 3D");
456 | if (pattern.size() > 0)
457 | injector::MakeRET(pattern.get(0).get(0), 0, true);
458 |
459 | // RETN - skip files.txt hash check [v1000 - v1001]
460 | pattern = hook::pattern("81 EC ? ? ? ? A1 ? ? ? ? 33 C4 89 84 24 ? ? ? ? 83 3D ? ? ? ? ? 0F 85 ? ? ? ? 83 3D");
461 | if (pattern.size() > 0)
462 | injector::MakeRET(pattern.get(0).get(0), 0, true);
463 |
464 | // RETN - skip files.txt hash check [v1000 - v1001]
465 | pattern = hook::pattern("81 EC ? ? ? ? A1 ? ? ? ? 33 C4 89 84 24 ? ? ? ? 56 68 ? ? ? ? 68 ? ? ? ? 68");
466 | if (pattern.size() > 0)
467 | injector::MakeRET(pattern.get(0).get(0), 0, true);
468 |
469 | // JMP [v1002 - v1004]
470 | pattern = hook::pattern("3B 56 20 74 19");
471 | if (pattern.size() > 0)
472 | {
473 | injector::WriteMemory(pattern.get(0).get(3), 0xE9, true);
474 | injector::WriteMemory(pattern.get(0).get(4), 0x16, true);
475 | }
476 |
477 | // NOP - another files.txt hash check [v1000 - v1001]
478 | pattern = hook::pattern("74 F7 8B CE");
479 | if (pattern.size() > 0)
480 | injector::MakeNOP(pattern.get(0).get(0), 2, true);
481 |
482 | // RETN - remove connect to the RGSC [v1000 - v1001]
483 | pattern = hook::pattern("83 EC 68 A1 ? ? ? ? 33 C4 89 44 24 60 53 56 57 8B F1 68 ? ? ? ? 8D 44 24 2C 8D 4C 24 3C 68");
484 | if (pattern.size() > 0)
485 | injector::MakeRET(pattern.get(0).get(0), 0, true);
486 |
487 | // RETN 4 - remove connect to the RGSC [v1002 - v1005]
488 | pattern = hook::pattern("83 EC 64 A1 ? ? ? ? 33 C4 89 44 24 60 8B 44 24 68 53 56 8B F1 8B 08 3B 4E 0C 57 0F 85");
489 | if (pattern.size() > 0)
490 | injector::MakeRET(pattern.get(0).get(0), 4, true);
491 |
492 | // data integrity checks VDS102 [v1002 - v1004]
493 | pattern = hook::pattern("C7 06 ? ? ? ? 74 19");
494 | if (pattern.size() > 0)
495 | injector::WriteMemory(pattern.get(0).get(6), 0xEB, true); //jmp
496 |
497 | // data integrity checks VDS102 [v1005 - v1008]
498 | pattern = hook::pattern("83 C4 ? 3B 46 20");
499 | if (pattern.size() > 0)
500 | injector::WriteMemory(pattern.get(0).get(12), 0xEB, true); //jmp
501 |
502 | // data integrity checks VDS100 [v1006 - v1008]
503 | pattern = hook::pattern("75 28 6A 00 6A 00 68 ? ? ? ? E8 ? ? ? ? 83 C4 0C");
504 | if (pattern.size() > 0)
505 | injector::WriteMemory(pattern.get(0).get(0), 0xEB, true); //jmp
506 |
507 | // NOP - RGSC initialization check [v1000 - v1001]
508 | pattern = hook::pattern("74 EC 38 5E 06 74 E7 68");
509 | if (pattern.size() > 0)
510 | {
511 | injector::MakeNOP(pattern.get(0).get(0), 2, true);
512 | injector::MakeNOP(pattern.get(0).get(5), 2, true);
513 | }
514 |
515 | // NOP - RGSC initialization check [v1002 - v1004]
516 | pattern = hook::pattern("74 C4 8B 07 3B 46 18 75 BD 68");
517 | if (pattern.size() > 0)
518 | injector::MakeNOP(pattern.get(0).get(0), 2, true);
519 |
520 | // NOP - last RGSC init check [v1002 - v1004]
521 | pattern = hook::pattern("3B 56 18 0F 85");
522 | if (pattern.size() > 0)
523 | injector::MakeNOP(pattern.get(0).get(3), 6, true);
524 |
525 | // NOP - last RGSC init check [v1005]
526 | pattern = hook::pattern("3B 4E 18 0F 85");
527 | if (pattern.size() > 0)
528 | injector::MakeNOP(pattern.get(0).get(3), 6, true);
529 |
530 | // NOP - last RGSC init check [v1006 - v1008]
531 | pattern = hook::pattern("0F 85 ? ? ? ? 8B 8C 24 ? ? ? ? 5F 5E 5B 33 CC C6");
532 | if (pattern.size() > 0)
533 | injector::MakeNOP(pattern.get(0).get(0), 6, true);
534 |
535 | // RGSC initialization check [v1002 - v1004]
536 | pattern = hook::pattern("3B 46 18 75 BD");
537 | if (pattern.size() > 0)
538 | {
539 | injector::WriteMemory(pattern.get(0).get(3), 0xC033, true); //XOR eax, eax
540 | injector::WriteMemory(pattern.get(0).get(10), 0xA390, true); //NOP; MOV [], eax
541 | }
542 |
543 | // EFC20 [v1003 - v1004]
544 | pattern = hook::pattern("57 51 C7 44 24 ? ? ? ? ? C7 44 24 ? ? ? ? ? C7 44 24");
545 | if (pattern.size() > 0)
546 | injector::MakeNOP(pattern.get(0).get(1), 0x1BF, true);
547 |
548 | // fix messed sequences
549 | {
550 | pattern = hook::pattern("A1 ? ? ? ? 85 C0 8B 0D ? ? ? ? 75");
551 | if (pattern.size() > 0)
552 | {
553 | injector::WriteMemory(pattern.get(0).get(0), 0x90C301B0, true); // 0xBAC160 mov al, 1; retn
554 | injector::WriteMemory(pattern.get(1).get(0), 0x90C301B0, true); // 0xBAC190 mov al, 1; retn
555 | }
556 |
557 | pattern = hook::pattern("33 C0 83 3D ? ? ? ? 01 0F 94 C0 C3");
558 | if (pattern.size() > 0)
559 | {
560 | injector::WriteMemory(pattern.get(pattern.size() - 2).get(0), 0x90C301B0, true); // 0xBAC180 mov al, 1; retn
561 | injector::WriteMemory(pattern.get(pattern.size() - 1).get(0), 0x90C301B0, true); // 0xBAC1C0 mov al, 1; retn
562 | }
563 | }
564 |
565 | // skip RGSC connect and EFC checks [v1005 - v1008]
566 | pattern = hook::pattern("8B 56 1C 3B 56 20 ? ? 6A 00 6A 00");
567 | if (pattern.size() > 0 && jmp_efc != nullptr)
568 | {
569 | injector::WriteMemory(pattern.get(0).get(0), 0xC033, true); // xor eax, eax - address of the RGSC object
570 | injector::MakeJMP(pattern.get(0).get(2), jmp_efc, true);
571 | }
572 |
573 | // NOP; MOV [g_rgsc], eax [v1000 - v1008]
574 | pattern = hook::pattern("89 35 ? ? ? ? E8 ? ? ? ? 83 C4 04 84 C0");
575 | if (pattern.size() > 0)
576 | {
577 | if (gv_not({ 1000, 1010 }))
578 | injector::WriteMemory(pattern.get(0).get(0), 0xA390, true);
579 | else
580 | injector::WriteMemory(pattern.get(0).get(1), 0x1D, true);
581 | }
582 |
583 | //skip missing tests [v1005]
584 | {
585 | pattern = hook::pattern("8B 50 2C 3B 50 30");
586 | if (pattern.size() > 0)
587 | {
588 | injector::WriteMemory(pattern.get(0).get(0), 0xC033, true);
589 | injector::MakeNOP(pattern.get(0).get(2), 4, true);
590 | }
591 |
592 | pattern = hook::pattern("8B 48 2C 83 EC 78");
593 | if (pattern.size() > 0)
594 | {
595 | injector::MakeNOP(pattern.get(0).get(0), 3, true);
596 | injector::MakeNOP(pattern.get(0).get(6), 11, true);
597 |
598 | }
599 |
600 | pattern = hook::pattern("8B 48 2C 83 EC 74");
601 | if (pattern.size() > 0)
602 | {
603 | injector::MakeNOP(pattern.get(0).get(0), 3, true);
604 | injector::MakeNOP(pattern.get(0).get(6), 11, true);
605 |
606 | }
607 |
608 | pattern = hook::pattern("8B 48 2C 3B 48 30 74 03");
609 | if (pattern.size() > 0)
610 | injector::MakeNOP(pattern.get(0).get(0), 11, true);
611 |
612 | pattern = hook::pattern("8B 48 2C 3B 48 30 53");
613 | if (pattern.size() > 0)
614 | injector::MakeNOP(pattern.get(0).get(0), 32, true);
615 |
616 | pattern = hook::pattern("8B 48 2C 3B 48 30 74 11");
617 | if (pattern.size() > 0)
618 | injector::MakeNOP(pattern.get(0).get(0), 25, true);
619 |
620 | pattern = hook::pattern("8B 48 2C 3B 48 30 75 DC 8D 54 24 18");
621 | if (pattern.size() > 0)
622 | injector::MakeNOP(pattern.get(0).get(0), 8, true);
623 |
624 | pattern = hook::pattern("8B 48 2C 3B 48 30 75 DC 8D 54 24 1C");
625 | if (pattern.size() > 0)
626 | injector::MakeNOP(pattern.get(0).get(0), 8, true);
627 |
628 | pattern = hook::pattern("8B 0D ? ? ? ? 8B 51 2C");
629 | if (pattern.size() > 0)
630 | injector::MakeNOP(pattern.get(0).get(0), 14, true);
631 | }
632 |
633 | //skip missing tests [v1003 - v1005]
634 | pattern = hook::pattern("51 53 55 56 57 8B 3D ? ? ? ? 8B 47 2C");
635 | if (pattern.size() > 0)
636 | injector::MakeRET(pattern.get(0).get(0), 0, true);
637 |
638 | //skip missing tests [v1005 - v1008]
639 | pattern = hook::pattern("3B C7 0F 85 ? ? ? ? 85 C0");
640 | if (pattern.size() > 0)
641 | injector::MakeNOP(pattern.get(0).get(0), 16, true);
642 |
643 | //skip missing tests [v1005 - v1008]
644 | pattern = hook::pattern("0F 85 ? ? ? ? 85 C0 0F 84 ? ? ? ? E8 ? ? ? ? 85 D2");
645 | if (pattern.size() > 0)
646 | injector::MakeNOP(pattern.get(0).get(0), 14, true);
647 |
648 | // skip missing tests [v1006 - v1007]
649 | pattern = hook::pattern("83 EC 24 A1 ? ? ? ? 8B 48 14 53 55 56 57");
650 | if (pattern.size() > 0)
651 | injector::WriteMemory(pattern.get(0).get(0), 0x90C3C033, true);
652 |
653 | // skip missing tests [v1006 - v1007]
654 | pattern = hook::pattern("83 EC 08 8B 0D ? ? ? ? 8B 51 14 8D 04 24 50 6A 00 52");
655 | if (pattern.size() > 0)
656 | injector::WriteMemory(pattern.get(0).get(0), 0x90C3C033, true);
657 |
658 | /*
659 | //is this needed? I'm not sure
660 | //patchEflc1();
661 | memset((BYTE*)(0x49412C + dwLoadOffset), 0x90, 24);
662 | // >> TEST
663 | *(DWORD*)(0x474FD0 + dwLoadOffset) = 0x90C3C033; // xor eax, eax; retn
664 | *(BYTE*)(0x7CAD20 + dwLoadOffset) = 0xC3;
665 |
666 | //patchEflc2();
667 | memset((BYTE*)(0x493D4C + dwLoadOffset), 0x90, 24);
668 | */
669 |
670 | // DLC
671 | // token activation [v1006 - v1007]
672 | pattern = hook::pattern("8B 54 24 04 52 A3");
673 | if (pattern.size() > 0)
674 | injector::WriteMemory(pattern.get(0).get(0), 0xC9EB, true);
675 |
676 | // token activation [v1006 - v1007]
677 | pattern = hook::pattern("83 F8 01 75 30 A3");
678 | if (pattern.size() > 0)
679 | {
680 | injector::WriteMemory(pattern.get(0).get(0), 0xB8, true);
681 | injector::WriteMemory(pattern.get(0).get(1), 1, true);
682 | }
683 |
684 | // skip first-time play ask [v1006 - v1008]
685 | pattern = hook::pattern("C7 05 ? ? ? ? 03 00 00 00 33 C0 5E 59 C3 C7");
686 | if (pattern.size() > 0)
687 | injector::WriteMemory(pattern.get(0).get(0), 0x0DEB, true);
688 |
689 | // DFA (EFLC 1100/1110: Unhandled exception at 0x00426440 in EFLC.exe: 0xC0000005: Access violation reading location 0x00000014.)
690 | if (gv_not({ 1100, 1110 }))
691 | {
692 | //[v1006 - v1008]
693 | pattern = hook::pattern("E8 ? ? ? ? 83 C4 0C 83 F8 01 74 11 6A 00 6A 00");
694 | if (pattern.size() > 0)
695 | injector::MakeCALL(pattern.get_first(0), dfaInit, true);
696 |
697 | //[v1005 - v1008]
698 | pattern = hook::pattern("55 8B EC 6A 00 E8");
699 | if (pattern.size() > 0)
700 | injector::MakeJMP(pattern.get(0).get(0), CreateFileHook, true);
701 |
702 | //[v1005 - v1008]
703 | pattern = hook::pattern("6A 00 E8 ? ? ? ? 83 F8 01 59 75 17 FF 15 ? ? ? ? 50 B9 ? ? ? ? E8 ? ? ? ? 8B 40 08 FF 60 0C 33 C0 C3");
704 | if (pattern.size() > 0)
705 | injector::MakeJMP(pattern.get(0).get(0), CloseHandleHook, true);
706 |
707 | //[v1005 - v1008]
708 | pattern = hook::pattern("6A 00 E8 ? ? ? ? 83 F8 01 59 75 17 FF 15 ? ? ? ? 50 B9 ? ? ? ? E8 ? ? ? ? 8B 40 08 FF 60 ? 83 C8 FF C3");
709 | if (pattern.size() > 0)
710 | injector::MakeJMP(pattern.get(0).get(0), SetFilePointerHook, true);
711 |
712 | //[v1005 - v1008]
713 | pattern = hook::pattern("56 8B 74 24 0C 8D 46 04 50 FF 74 24 0C E8 ? ? ? ? 59 59 33 C9 83 F8 FF 0F 95 C1 89 06 5E 8B C1 C3");
714 | if (pattern.size() > 0)
715 | injector::MakeJMP(pattern.get(0).get(0), GetFileSizeExHook, true);
716 |
717 | //[v1005 - v1008]
718 | pattern = hook::pattern("55 8B EC 51 51 53 56 8B C5");
719 | if (pattern.size() > 0)
720 | injector::MakeJMP(pattern.get(0).get(0), ReadFileHook, true);
721 |
722 | //[v1006 - v1008]
723 | pattern = hook::pattern("A1 ? ? ? ? B9 A7 FA DC 5C F7 E1 8B 0D ? ? ? ? 33 F6");
724 | if (pattern.size() > 0)
725 | {
726 | injector::MakeJMP(pattern.get(0).get(0), hook::pattern("80 BC 24 ? ? ? ? ? 6A 00").get(0).get(0), true);
727 | injector::MakeJMP(pattern.get(1).get(0), hook::pattern("6A 00 68 80 00 00 00 6A 03 6A 00 6A 01 68 00 00 00 80 57").get(0).get(0), true);
728 | }
729 | }
730 |
731 | // windows message id [v1000 - v1008]
732 | pattern = hook::pattern("A1 ? ? ? ? 8B 0D ? ? ? ? 68 ? ? ? ? 53 50 51 FF 15");
733 | injector::WriteMemory(*pattern.get_first(1), WM_ROM, true);
734 | injector::WriteMemory(*pattern.get_first(7), CreateROMWindow(), true); // window handle
735 |
736 | // SendMessage call to this window that ends up freezing the audio update thread and resulting in a deadlock [v1000 - v1008]
737 | pattern = hook::pattern("FF 15 ? ? ? ? E8 ? ? ? ? E8 ? ? ? ? E8 ? ? ? ? E8 ? ? ? ? B9 ? ? ? ? E8 ? ? ? ? B9 ? ? ? ? E8 ? ? ? ? 83 7C 24");
738 | injector::MakeNOP(pattern.get_first(0), 6, true);
739 | injector::MakeCALL(pattern.get_first(0), SendMessageFakie, true);
740 |
741 | if (bSkipWebConnect)
742 | {
743 | // [v1000 - v1001]
744 | pattern = hook::pattern("FF 15 ? ? ? ? 85 C0 75 18");
745 | if (pattern.size() > 0)
746 | injector::MakeNOP(pattern.get(0).get(8), 2, true); //InternetGetConnectedState
747 |
748 | // [v1002 - v1005]
749 | pattern = hook::pattern("FF 15 ? ? ? ? 85 C0 75 04 32");
750 | if (pattern.size() > 0)
751 | injector::MakeNOP(pattern.get(0).get(8), 2, true); //InternetGetConnectedState
752 |
753 | // [v1006 - v1008]
754 | pattern = hook::pattern("FF 15 ? ? ? ? 85 C0 75 07 A0");
755 | if (pattern.size() > 0)
756 | injector::MakeNOP(pattern.get(0).get(8), 2, true); //InternetGetConnectedState
757 |
758 | // [v1003 - v1008]
759 | pattern = hook::pattern("81 EC ? ? ? ? A1 ? ? ? ? 33 C4 89 84 24 ? ? ? ? 53 ? 6A 3C 33 DB 8D 44 24 ? 53 50 E8"); // health check
760 | if (pattern.size() > 0)
761 | injector::WriteMemory(pattern.get(0).get(0), 0xC3, true);
762 | }
763 |
764 | if (bSkipIntro)
765 | {
766 | //if (isEFLC)
767 | //{
768 | // pattern = hook::pattern("74 ? 80 3D ? ? ? ? 00 74 ? E8 ? ? ? ? 0F");
769 | // injector::WriteMemory(pattern.get(0).get(0), 0xEB, true);
770 | //}
771 | //else
772 | {
773 | // [v1000 - v1008]
774 | //since iv hangs with the code above, I'll just zero the duration of loadscreens
775 | pattern = hook::pattern("89 91 ? ? ? ? 8D 44 24 ? 68 ? ? ? ? 50");
776 | struct Loadsc
777 | {
778 | void operator()(injector::reg_pack& regs)
779 | {
780 | *(int32_t*)®s.ecx *= 400;
781 | if (regs.edx < 8000)
782 | regs.edx = 0;
783 | }
784 | }; injector::MakeInline(pattern.get_first(-6), pattern.get_first(0));
785 | }
786 | }
787 |
788 | if (bSkipMenu)
789 | {
790 | pattern = hook::pattern("83 F8 03 75 ? A1 ? ? ? ? 80 88 ? ? ? ? ? 84 DB 74 0A 6A 00 E8 ? ? ? ? 83 C4 04 5F 5E");
791 | if (pattern.size() > 0)
792 | {
793 | // [v1002 - v1008]
794 | hbsub_7870A0.fun = injector::MakeCALL(pattern.get_first(23), sub_7870A0).get();
795 | }
796 | else
797 | {
798 | // [v1000]
799 | pattern = hook::pattern("6A 00 E8 ? ? ? ? 83 C4 04 8B 8C 24 ? ? ? ? 5F 5E 5D 5B 33 CC E8 ? ? ? ? 81 C4 ? ? ? ? C3");
800 | if (pattern.size() > 0)
801 | hbsub_7870A0.fun = injector::MakeCALL(pattern.get(0).get(2), sub_7870A0).get();
802 | else
803 | {
804 | // [v1001]
805 | pattern = hook::pattern("6A 00 E8 ? ? ? ? 83 C4 04 8B 8C 24 ? ? ? ? 5F 5E 5B 33 CC E8 ? ? ? ? 8B E5 5D C3");
806 | if (pattern.size() > 0)
807 | hbsub_7870A0.fun = injector::MakeCALL(pattern.get(0).get(2), sub_7870A0).get();
808 | }
809 | }
810 | }
811 |
812 | if (bDoNotPauseOnMinimize)
813 | {
814 | // [v1002 - v1008]
815 | pattern = hook::pattern("75 ? 8B 0D ? ? ? ? 51 FF 15 ? ? ? ? 85 C0 75 ? 8B 15"); //0x402D5A
816 | if (pattern.size() > 0)
817 | injector::MakeNOP(pattern.get(0).get(0), 2, true);
818 | }
819 |
820 | if (bBorderlessWindowed)
821 | {
822 | // [v1000 - v1008]
823 | pattern = hook::pattern("FF 15 ? ? ? ? 5F 5E 5D 5B 83 C4 10 C2 10 00");
824 | injector::MakeNOP(pattern.count(2).get(0).get(0), 6, true);
825 | injector::MakeCALL(pattern.count(2).get(0).get(0), DefWindowProcWProxy, true);
826 | injector::MakeNOP(pattern.count(2).get(1).get(0), 6, true);
827 | injector::MakeCALL(pattern.count(2).get(1).get(0), DefWindowProcAProxy, true);
828 | }
829 |
830 | if (bRemoveRegistryPathDependency)
831 | {
832 | IMAGE_IMPORT_DESCRIPTOR* pImports = (IMAGE_IMPORT_DESCRIPTOR*)(dwLoadOffset + pNtHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress);
833 | size_t nNumImports = pNtHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].Size / sizeof(IMAGE_IMPORT_DESCRIPTOR) - 1;
834 |
835 | auto pRegOpenKeyA = (size_t)GetProcAddress(GetModuleHandle(TEXT("ADVAPI32.DLL")), "RegOpenKeyA");
836 | auto pRegQueryValueExA = (size_t)GetProcAddress(GetModuleHandle(TEXT("ADVAPI32.DLL")), "RegQueryValueExA");
837 | auto pRegOpenKeyExA = (size_t)GetProcAddress(GetModuleHandle(TEXT("ADVAPI32.DLL")), "RegOpenKeyExA");
838 |
839 | static auto getSection = [](const PIMAGE_NT_HEADERS nt_headers, unsigned section) -> PIMAGE_SECTION_HEADER
840 | {
841 | return reinterpret_cast(
842 | (UCHAR*)nt_headers->OptionalHeader.DataDirectory +
843 | nt_headers->OptionalHeader.NumberOfRvaAndSizes * sizeof(IMAGE_DATA_DIRECTORY) +
844 | section * sizeof(IMAGE_SECTION_HEADER));
845 | };
846 |
847 | static auto getSectionEnd = [](IMAGE_NT_HEADERS* ntHeader, size_t inst) -> auto
848 | {
849 | auto sec = getSection(ntHeader, ntHeader->FileHeader.NumberOfSections - 1);
850 | auto secSize = max(sec->SizeOfRawData, sec->Misc.VirtualSize);
851 | auto end = inst + max(sec->PointerToRawData, sec->VirtualAddress) + secSize;
852 | return end;
853 | };
854 |
855 | auto PatchIAT = [&](size_t start, size_t end, size_t exe_end)
856 | {
857 | for (size_t i = 0; i < nNumImports; i++)
858 | {
859 | if (dwLoadOffset + (pImports + i)->FirstThunk > start && !(end && dwLoadOffset + (pImports + i)->FirstThunk > end))
860 | end = dwLoadOffset + (pImports + i)->FirstThunk;
861 | }
862 |
863 | if (!end) { end = start + 0x100; }
864 | if (end > exe_end)
865 | {
866 | start = dwLoadOffset;
867 | end = exe_end;
868 | }
869 |
870 | for (auto i = start; i < end; i += sizeof(size_t))
871 | {
872 | DWORD dwProtect[2];
873 | VirtualProtect((size_t*)i, sizeof(size_t), PAGE_EXECUTE_READWRITE, &dwProtect[0]);
874 |
875 | auto ptr = *(size_t*)i;
876 | if (!ptr)
877 | continue;
878 |
879 | if (ptr == pRegOpenKeyA)
880 | {
881 | *(size_t*)i = (size_t)CustomRegOpenKeyA;
882 | }
883 | else if (ptr == pRegQueryValueExA)
884 | {
885 | *(size_t*)i = (size_t)CustomRegQueryValueExA;
886 | }
887 | else if (ptr == pRegOpenKeyExA)
888 | {
889 | *(size_t*)i = (size_t)CustomRegOpenKeyExA;
890 | }
891 |
892 | VirtualProtect((size_t*)i, sizeof(size_t), dwProtect[0], &dwProtect[1]);
893 | }
894 | };
895 |
896 | auto end = getSectionEnd(pNtHeader, dwLoadOffset);
897 |
898 | for (size_t i = 0; i < nNumImports; i++)
899 | {
900 | if ((size_t)(dwLoadOffset + (pImports + i)->Name) < end)
901 | {
902 | if (!_stricmp((const char*)(dwLoadOffset + (pImports + i)->Name), "ADVAPI32.DLL"))
903 | {
904 | PatchIAT(dwLoadOffset + (pImports + i)->FirstThunk, 0, end);
905 | break;
906 | }
907 | }
908 | }
909 | }
910 |
911 | if (bVRAMFix)
912 | {
913 | // workaround for vram detection, so settings won't be locked out [v1000 - v1007]
914 | pattern = hook::pattern("E8 ? ? ? ? D9 E8 85 C0");
915 | if (pattern.size() > 0)
916 | hbGetVidMem.fun = injector::MakeCALL(pattern.get_first(0), GetVidMem).get();
917 |
918 | pattern = hook::pattern("E8 ? ? ? ? F3 0F 10 0D ? ? ? ? F3 0F 5C 0D");
919 | if (pattern.size() > 0)
920 | hbGetVidMem.fun = injector::MakeCALL(pattern.get_first(0), GetVidMem).get();
921 |
922 | pattern = hook::pattern("E8 ? ? ? ? F3 0F 10 05 ? ? ? ? 89 44 24 08 6A 01 8D 44 24");
923 | if (pattern.size() > 0)
924 | hbGetVidMem.fun = injector::MakeCALL(pattern.get_first(0), GetVidMem).get();
925 | }
926 |
927 | return 0;
928 | }
929 |
930 |
931 | BOOL APIENTRY DllMain(HMODULE /*hModule*/, DWORD reason, LPVOID /*lpReserved*/)
932 | {
933 | if (reason == DLL_PROCESS_ATTACH)
934 | {
935 | Init(NULL);
936 | }
937 | return TRUE;
938 | }
939 |
--------------------------------------------------------------------------------
/XLivelessAddon/includes/IniReader/.gitattributes:
--------------------------------------------------------------------------------
1 | # Auto detect text files and perform LF normalization
2 | * text=auto
3 |
4 | # Custom for Visual Studio
5 | *.cs diff=csharp
6 |
7 | # Standard to msysgit
8 | *.doc diff=astextplain
9 | *.DOC diff=astextplain
10 | *.docx diff=astextplain
11 | *.DOCX diff=astextplain
12 | *.dot diff=astextplain
13 | *.DOT diff=astextplain
14 | *.pdf diff=astextplain
15 | *.PDF diff=astextplain
16 | *.rtf diff=astextplain
17 | *.RTF diff=astextplain
18 |
--------------------------------------------------------------------------------
/XLivelessAddon/includes/IniReader/IniReader.h:
--------------------------------------------------------------------------------
1 | #ifndef INIREADER_H
2 | #define INIREADER_H
3 | #include "ini_parser.hpp"
4 | #include
5 | #include
6 |
7 | /*
8 | * String comparision functions, with case sensitive option
9 | */
10 |
11 | using std::strcmp;
12 |
13 | inline int strcmp(const char* str1, const char* str2, bool csensitive)
14 | {
15 | return (csensitive ? ::strcmp(str1, str2) : ::_stricmp(str1, str2));
16 | }
17 |
18 | inline int strcmp(const char* str1, const char* str2, size_t num, bool csensitive)
19 | {
20 | return (csensitive ? ::strncmp(str1, str2, num) : ::_strnicmp(str1, str2, num));
21 | }
22 |
23 | inline int compare(const std::string& str1, const std::string& str2, bool case_sensitive)
24 | {
25 | if (str1.length() == str2.length())
26 | return strcmp(str1.c_str(), str2.c_str(), case_sensitive);
27 | return (str1.length() < str2.length() ? -1 : 1);
28 | }
29 |
30 | inline int compare(const std::string& str1, const std::string& str2, size_t num, bool case_sensitive)
31 | {
32 | if (str1.length() == str2.length())
33 | return strcmp(str1.c_str(), str2.c_str(), num, case_sensitive);
34 | return (str1.length() < str2.length() ? -1 : 1);
35 | }
36 |
37 | inline int compare(const char* str1, const char* str2, bool case_sensitive)
38 | {
39 | return strcmp(str1, str2, case_sensitive);
40 | }
41 |
42 | inline int compare(const char* str1, const char* str2, size_t num, bool case_sensitive)
43 | {
44 | return strcmp(str1, str2, num, case_sensitive);
45 | }
46 |
47 | inline bool starts_with(const char* str, const char* prefix, bool case_sensitive)
48 | {
49 | while (*prefix)
50 | {
51 | bool equal;
52 | if (case_sensitive)
53 | equal = (*str++ == *prefix++);
54 | else
55 | equal = (::tolower(*str++) == ::tolower(*prefix++));
56 |
57 | if (!equal) return false;
58 | }
59 | return true;
60 | }
61 |
62 | inline bool ends_with(const char* str, const char* prefix, bool case_sensitive)
63 | {
64 | auto str2 = &str[strlen(str) - 1];
65 | auto prefix2 = &prefix[strlen(prefix) - 1];
66 |
67 | while (prefix2 >= prefix)
68 | {
69 | bool equal;
70 | if (case_sensitive)
71 | equal = (*str2-- == *prefix2--);
72 | else
73 | equal = (::tolower(*str2--) == ::tolower(*prefix2--));
74 |
75 | if (!equal) return false;
76 | }
77 | return true;
78 | }
79 |
80 | class CIniReader
81 | {
82 | private:
83 | std::string m_szFileName;
84 |
85 | public:
86 | linb::ini data;
87 |
88 | CIniReader()
89 | {
90 | SetIniPath("");
91 | }
92 |
93 | CIniReader(char* szFileName)
94 | {
95 | SetIniPath(szFileName);
96 | }
97 |
98 | CIniReader(const char* szFileName)
99 | {
100 | SetIniPath((char*)szFileName);
101 | }
102 |
103 | CIniReader(std::stringstream& ini_mem)
104 | {
105 | data.load_file(ini_mem);
106 | }
107 |
108 | bool operator==(CIniReader& ir)
109 | {
110 | if (data.size() != ir.data.size())
111 | return false;
112 |
113 | for (auto& section : data)
114 | {
115 | for (auto& key : data[section.first])
116 | {
117 | if (key.second != ir.data[section.first][key.first])
118 | return false;
119 | }
120 | }
121 | return true;
122 | }
123 |
124 | bool operator!=(CIniReader& ir)
125 | {
126 | return !(*this == ir);
127 | }
128 |
129 | bool CompareBySections(CIniReader& ir)
130 | {
131 | if (data.size() != ir.data.size())
132 | return false;
133 |
134 | for (auto& section : data)
135 | {
136 | if (ir.data.find(section.first) == ir.data.end())
137 | return false;
138 |
139 | if (section.second.size() != ir.data.find(section.first)->second.size())
140 | return false;
141 |
142 | if (section.first != ir.data.find(section.first)->first)
143 | return false;
144 | }
145 | return true;
146 | }
147 |
148 | bool CompareByValues(CIniReader& ir)
149 | {
150 | return *this == ir;
151 | }
152 |
153 | const std::string& GetIniPath()
154 | {
155 | return m_szFileName;
156 | }
157 |
158 | void SetIniPath()
159 | {
160 | SetIniPath("");
161 | }
162 |
163 | void SetIniPath(char* szFileName)
164 | {
165 | char buffer[MAX_PATH];
166 | HMODULE hm = NULL;
167 | GetModuleHandleExA(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, (LPCSTR)&ends_with, &hm);
168 | GetModuleFileNameA(hm, buffer, sizeof(buffer));
169 | std::string modulePath = buffer;
170 |
171 | if (strchr(szFileName, ':') != NULL)
172 | {
173 | m_szFileName = szFileName;
174 | }
175 | else if (std::string(szFileName).length() == 0)
176 | {
177 | m_szFileName = modulePath.substr(0, modulePath.find_last_of('.')) + ".ini";
178 | }
179 | else
180 | {
181 | m_szFileName = modulePath.substr(0, modulePath.rfind('\\') + 1) + szFileName;
182 | }
183 |
184 | data.load_file(m_szFileName);
185 | }
186 |
187 | int ReadInteger(char* szSection, char* szKey, int iDefaultValue)
188 | {
189 | try {
190 | auto str = data.get(szSection, szKey, std::to_string(iDefaultValue));
191 | return std::stoi(str, nullptr, starts_with(str.c_str(), "0x", false) ? 16 : 10);
192 | }
193 | catch (...) {
194 | return iDefaultValue;
195 | }
196 | }
197 |
198 | float ReadFloat(char* szSection, char* szKey, float fltDefaultValue)
199 | {
200 | try {
201 | return (float)atof(data.get(szSection, szKey, std::to_string(fltDefaultValue)).c_str());
202 | }
203 | catch (...) {
204 | return fltDefaultValue;
205 | }
206 | }
207 |
208 | bool ReadBoolean(char* szSection, char* szKey, bool bolDefaultValue)
209 | {
210 | try {
211 | auto& config = data[szSection];
212 | if (config.count(szKey))
213 | {
214 | if (config[szKey].size() == 1) return config[szKey].front() != '0';
215 | return !!compare(config[szKey], "false", false);
216 | }
217 | }
218 | catch (...) {
219 | return bolDefaultValue;
220 | }
221 | }
222 |
223 | char* ReadString(char* szSection, char* szKey, const char* szDefaultValue)
224 | {
225 | char* szResult = new char[255];
226 | try {
227 | auto& config = data[szSection];
228 | if (config.count(szKey))
229 | {
230 | if (config[szKey].at(0) == '\"' || config[szKey].at(0) == '\'')
231 | config[szKey].erase(0, 1);
232 |
233 | if (config[szKey].at(config[szKey].size() - 1) == '\"' || config[szKey].at(config[szKey].size() - 1) == '\'')
234 | config[szKey].erase(config[szKey].size() - 1);
235 |
236 | strcpy(szResult, config[szKey].c_str());
237 | return szResult;
238 | }
239 | }
240 | catch (...) { }
241 | strcpy(szResult, szDefaultValue);
242 | return szResult;
243 | }
244 |
245 | std::string ReadString(char* szSection, char* szKey, std::string szDefaultValue)
246 | {
247 | char* str = ReadString(szSection, szKey, szDefaultValue.c_str());
248 | std::string* szResult = new std::string(str);
249 | return *szResult;
250 | }
251 |
252 | void WriteInteger(char* szSection, char* szKey, int iValue, bool useparser = false)
253 | {
254 | if (useparser)
255 | {
256 | data.set(szSection, szKey, std::to_string(iValue));
257 | data.write_file(m_szFileName);
258 | }
259 | else
260 | {
261 | char szValue[255];
262 | _snprintf_s(szValue, 255, "%s%d", " ", iValue);
263 | WritePrivateProfileStringA(szSection, szKey, szValue, m_szFileName.c_str());
264 | }
265 | }
266 |
267 | void WriteFloat(char* szSection, char* szKey, float fltValue, bool useparser = false)
268 | {
269 | if (useparser)
270 | {
271 | data.set(szSection, szKey, std::to_string(fltValue));
272 | data.write_file(m_szFileName);
273 | }
274 | else
275 | {
276 | char szValue[255];
277 | _snprintf_s(szValue, 255, "%s%f", " ", fltValue);
278 | WritePrivateProfileStringA(szSection, szKey, szValue, m_szFileName.c_str());
279 | }
280 | }
281 |
282 | void WriteBoolean(char* szSection, char* szKey, bool bolValue, bool useparser = false)
283 | {
284 | if (useparser)
285 | {
286 | data.set(szSection, szKey, std::to_string(bolValue));
287 | data.write_file(m_szFileName);
288 | }
289 | else
290 | {
291 | char szValue[255];
292 | _snprintf_s(szValue, 255, "%s%s", " ", bolValue ? "True" : "False");
293 | WritePrivateProfileStringA(szSection, szKey, szValue, m_szFileName.c_str());
294 | }
295 | }
296 |
297 | void WriteString(char* szSection, char* szKey, char* szValue, bool useparser = false)
298 | {
299 | if (useparser)
300 | {
301 | data.set(szSection, szKey, szValue);
302 | data.write_file(m_szFileName);
303 | }
304 | else
305 | {
306 | WritePrivateProfileStringA(szSection, szKey, szValue, m_szFileName.c_str());
307 | }
308 | }
309 | };
310 |
311 | #endif //INIREADER_H
312 |
--------------------------------------------------------------------------------
/XLivelessAddon/includes/IniReader/LICENSE.txt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2013-2015 Denilson das Mercês Amorim
3 | *
4 | * This software is provided 'as-is', without any express or implied
5 | * warranty. In no event will the authors be held liable for any damages
6 | * arising from the use of this software.
7 | *
8 | * Permission is granted to anyone to use this software for any purpose,
9 | * including commercial applications, and to alter it and redistribute it
10 | * freely, subject to the following restrictions:
11 | *
12 | * 1. The origin of this software must not be misrepresented; you must not
13 | * claim that you wrote the original software. If you use this software
14 | * in a product, an acknowledgment in the product documentation would be
15 | * appreciated but is not required.
16 | *
17 | * 2. Altered source versions must be plainly marked as such, and must not be
18 | * misrepresented as being the original software.
19 | *
20 | * 3. This notice may not be removed or altered from any source
21 | * distribution.
22 | *
23 | */
--------------------------------------------------------------------------------
/XLivelessAddon/includes/IniReader/README.md:
--------------------------------------------------------------------------------
1 | IniReader
2 | ----------------
3 | Sample:
4 |
5 | #include "stdafx.h"
6 | #include "IniReader\IniReader.h"
7 | #include
8 | #include
9 | #include
10 |
11 | int main()
12 | {
13 | CIniReader ini1("ini1.ini");
14 | ini1.WriteInteger("MAIN", "INTEGER", 33);
15 | ini1.WriteFloat("MAIN", "FLOAT", 35.5f);
16 | ini1.WriteBoolean("MAIN", "BOOL", false);
17 | ini1.WriteString("MAIN", "STRING", "text");
18 |
19 | CIniReader ini2("ini2.ini");
20 | ini2.WriteInteger("MAIN", "INTEGER", 33);
21 | ini2.WriteFloat("MAIN", "FLOAT", 35.5f);
22 | ini2.WriteBoolean("MAIN", "BOOL", false);
23 | ini2.WriteString("MAIN", "STRING", "text");
24 |
25 | std::cout << ini1.ReadInteger("MAIN", "INTEGER", 0) << std::endl;
26 | std::cout << ini2.ReadString("MAIN", "STRING", "") << std::endl;
27 |
28 | if (ini1 == ini2)
29 | std::cout << "ini1 and ini2 are the same." << std::endl;
30 |
31 | std::ifstream file("ini1.ini", std::ios::in);
32 | std::stringstream ini_mem;
33 | ini_mem << file.rdbuf();
34 |
35 | CIniReader mem_ini(ini_mem);
36 |
37 | mem_ini.WriteInteger("MAIN", "INTEGER", 0);
38 |
39 | if (!mem_ini.CompareByValues(ini2))
40 | std::cout << "mem_ini and ini2 are different." << std::endl;
41 |
42 | return 0;
43 | }
44 |
45 | Result:
46 |
47 | 
--------------------------------------------------------------------------------
/XLivelessAddon/includes/IniReader/ini_parser.hpp:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2013-2015 Denilson das Mercês Amorim
3 | * Copyright (c) 2017 ThirteenAG
4 | *
5 | * This software is provided 'as-is', without any express or implied
6 | * warranty. In no event will the authors be held liable for any damages
7 | * arising from the use of this software.
8 | *
9 | * Permission is granted to anyone to use this software for any purpose,
10 | * including commercial applications, and to alter it and redistribute it
11 | * freely, subject to the following restrictions:
12 | *
13 | * 1. The origin of this software must not be misrepresented; you must not
14 | * claim that you wrote the original software. If you use this software
15 | * in a product, an acknowledgment in the product documentation would be
16 | * appreciated but is not required.
17 | *
18 | * 2. Altered source versions must be plainly marked as such, and must not be
19 | * misrepresented as being the original software.
20 | *
21 | * 3. This notice may not be removed or altered from any source
22 | * distribution.
23 | *
24 | */
25 | #ifndef LINB_INI_PARSER_HPP
26 | #define LINB_INI_PARSER_HPP
27 |
28 | /*
29 | * STL-like INI Container
30 | */
31 |
32 | #include // for std::string
33 | #include