Registration Code (Part 1): w%kQ6
Registration Code (Part 2): b<#$1[*(cw~
In order to register on this forum, you must use the codes above. Combine them into one code (copy paste).

Resident Evil 3 - INI File Hook

Topics regarding the Resident Evil game series.
Post Reply
User avatar
atom0s
Site Admin
Posts: 397
Joined: Sun Jan 04, 2015 11:23 pm
Location: 127.0.0.1
Contact:

Resident Evil 3 - INI File Hook

Post by atom0s » Wed Nov 22, 2017 12:45 am

As part of a project I was helping with recently that landed up falling through due to various reasons, here is some information about Resident Evil 3.

INI File Hook

File: ResidentEvil3.exe
CRC32: 0x929F6CAE
Type: Hook
Purpose: Allows the game to run from unpacked files, allows altering the paths to load files from, allows altering save file location.
Pattern: 8B4424148B4C24108B54240C68

By default, the game will attempt to load the packed files as normal. This can be seen happening here in the game:
  1. .text:00404064                 mov     ecx, [esi+0F8h]
  2. .text:0040406A                 mov     eax, [ecx]
  3. .text:0040406C                 call    dword ptr [eax+4]
  4. .text:0040406F                 test    eax, eax
  5. .text:00404071                 jz      loc_40411F
  6. .text:00404077                 lea     ecx, [esp+868h+ReturnedString]
  7. .text:0040407B                 push    400h            ; nSize              ;<== Size of the lpReturnedString buffer.
  8. .text:00404080                 push    ecx             ; lpReturnedString   ;<== Buffer to hold the returned value.
  9. .text:00404081                 push    offset Default  ; lpDefault          ;<== The default value to use if not found in the ini.
  10. .text:00404086                 push    offset aMovie   ; "Movie"            ;<== The key to read the value of.
  11. .text:0040408B                 push    offset AppName  ; "General"          ;<== The section to read the value from.
  12. .text:00404090                 call    sub_404330                           ;<== Read the given data from the ini file.
  13. .text:00404095                 mov     al, [esp+87Ch+ReturnedString]

In this case, to allow the game to run fully unpacked (data file wise) in this case, you need to change the default value here to something else.
By default, the game uses no default value here. If no value is given it will use hard-coded internal paths to the data files/movies.

We can do a few things here to allow the game to run with unpacked data/movie files. You can patch the "push" for the lpDefault param to "zmovie". This will force the game to read the movies from the zmovie folder and allow the game to read data files in an unpacked format.

Since we also want to override other things such as the save file path and even the overall data path for the unpacked files, we can hook the ini reading function instead. The main function used to read the ini file is:
  1. .text:00404330 ; int __cdecl sub_404330(LPCSTR lpAppName, LPCSTR lpKeyName, LPCSTR lpDefault, LPSTR lpReturnedString, DWORD nSize)
  2. .text:00404330 sub_404330      proc near               ; CODE XREF: sub_403910+5FEp
  3. .text:00404330                                         ; sub_403910+637p ...
  4. .text:00404330
  5. .text:00404330 lpAppName       = dword ptr  4
  6. .text:00404330 lpKeyName       = dword ptr  8
  7. .text:00404330 lpDefault       = dword ptr  0Ch
  8. .text:00404330 lpReturnedString= dword ptr  10h
  9. .text:00404330 nSize           = dword ptr  14h
  10. .text:00404330
  11. .text:00404330                 mov     eax, [esp+nSize]
  12. .text:00404334                 mov     ecx, [esp+lpReturnedString]
  13. .text:00404338                 mov     edx, [esp+lpDefault]
  14. .text:0040433C                 push    offset FileName ; ".\\bio3.ini"
  15. .text:00404341                 push    eax             ; nSize
  16. .text:00404342                 mov     eax, [esp+8+lpKeyName]
  17. .text:00404346                 push    ecx             ; lpReturnedString
  18. .text:00404347                 mov     ecx, [esp+0Ch+lpAppName]
  19. .text:0040434B                 push    edx             ; lpDefault
  20. .text:0040434C                 push    eax             ; lpKeyName
  21. .text:0040434D                 push    ecx             ; lpAppName
  22. .text:0040434E                 call    ds:GetPrivateProfileStringA
  23. .text:00404354                 retn
  24. .text:00404354 sub_404330      endp
Once hooked we can override the values of specific read attempts such as the movie path, the save path etc. Such as this for example:
  1.  
  2. /**
  3.  * Detour Prototypes
  4.  */
  5. extern "C"
  6. {
  7.     int32_t /**/(__cdecl *Real_ReadIniFile)(LPCSTR lpAppName, LPCSTR lpKeyName, LPCSTR lpDefault, LPSTR lpReturnedString, DWORD nSize) = nullptr;
  8. }
  9.  
  10. /**
  11.  * Detoured ini reader function callback.
  12.  * @param {LPCSTR} lpAppName - The ini section name to read the value from.
  13.  * @param {LPCSTR} lpKeyName - The ini key name to read the value of.
  14.  * @param {LPCSTR} lpDefault - The default value to use if the requested one is not found.
  15.  * @param {LPSTR} lpReturnedString - The output buffer to hold the read data.
  16.  * @param {DWORD} nSize - The size of the output buffer.
  17.  * @returns {int32_t} The size of the returned data, 0 if error.
  18.  */
  19. int32_t __cdecl Mine_ReadIniFile(LPCSTR lpAppName, LPCSTR lpKeyName, LPCSTR lpDefault, LPSTR lpReturnedString, DWORD nSize)
  20. {
  21.     // Call the original function..
  22.     auto ret = Real_ReadIniFile(lpAppName, lpKeyName, lpDefault, lpReturnedString, nSize);
  23.  
  24.     // Skip non-general section entries..
  25.     if (_strnicmp(lpAppName, "General", 7) != 0)
  26.         return ret;
  27.  
  28.     // Obtain the RE3RP path from the config file..
  29.     std::string modpath = RE3RP::Configurations::instance().GetString("RE3RP", "modpath");
  30.  
  31.     // Obtain the current path..
  32.     char currentPath[MAX_PATH] = { 0 };
  33.     ::GetCurrentDirectory(MAX_PATH, currentPath);
  34.  
  35.     //
  36.     // Patch: Movie Path
  37.     //
  38.     if (_strnicmp(lpKeyName, "Movie", 5) == 0)
  39.     {
  40.         // Obtain the movies path setting..
  41.         std::string moviePath = RE3RP::Configurations::instance().GetString("RE3RP_Paths", "movies");
  42.         if (moviePath.length() == 0)
  43.             moviePath = "zmovie";
  44.  
  45.         // Build the new path..
  46.         std::string path = currentPath;
  47.         path += "\\" + modpath + "\\";
  48.         path += moviePath;
  49.  
  50.         // Override the path..
  51.         memset(lpReturnedString, 0x00, nSize);
  52.         strcpy_s((char*)lpReturnedString, path.length() + 1, path.c_str());
  53.         ret = path.length();
  54.     }
  55.  
  56.     //
  57.     // Patch: Save Path
  58.     //
  59.     if (_strnicmp(lpKeyName, "Save", 4) == 0)
  60.     {
  61.         // Obtain the save path setting..
  62.         std::string savePath = RE3RP::Configurations::instance().GetString("RE3RP_Paths", "saves");
  63.         if (savePath.length() == 0)
  64.             savePath = "saves";
  65.  
  66.         // Build the new path..
  67.         std::string path = currentPath;
  68.         path += "\\" + modpath + "\\";
  69.         path += savePath;
  70.  
  71.         // Override the path..
  72.         memset(lpReturnedString, 0x00, nSize);
  73.         strcpy_s((char*)lpReturnedString, path.length() + 1, path.c_str());
  74.         ret = path.length();
  75.     }
  76.  
  77.     return ret;
  78. }

In this example, we are hooking the full function and looking for two specific read attempts:
- Movie = The location movie files are read from.
- Save = The location saved files are stored.

We alter the saved path location because the project this was part of was modding a lot of things in the game. Saved files could be affected in adverse ways that may not work with the stock game itself. We also don't want to corrupt old save files trying to be loaded into the patched game so instead, the path is relocated to use a new location for modded saves.
Derp~
Need a great web host? Check out: AnHonestHost.com


Donations can be made via Paypal:
https://www.paypal.me/atom0s
Post Reply

Who is online

Users browsing this forum: No registered users and 0 guests