diff --git a/ReadMe.md b/ReadMe.md index 4ee85a1..fe381d9 100644 --- a/ReadMe.md +++ b/ReadMe.md @@ -18,9 +18,12 @@ This **NGPCarMenu RBR plugin solves these problems** by doing following enhancem - Longer car model names in car specs window (as much there is room on the screen). - Longer car menu names (up to 30 chars). -Note! This plugin supports only the latest RichardBurnsRally_SSE.exe v1.02 version for Windows OS (haven't tested this under Wine/Linux, but the plugin probably works there also). +The plugin supports also **RBRTM Czech Tournament** plugin (V0.88). NGPCarMenu plugin shows the car preview image and car details in Shakedown and OnlineTournament "car selection" menus within RBRTM. + +Note! NGPCarMenu supports only the latest RichardBurnsRally_SSE.exe v1.02 version for Windows OS (haven't tested this under Wine/Linux, but the plugin probably works there also). ![](misc/NGPCarMenu_SelectCarMenu.png) +![](misc/NGPCarMenu_RBRTMMenu.png) ![](misc/NGPCarMenu_PluginMenu.png) ## Download @@ -30,12 +33,13 @@ Note! This plugin supports only the latest RichardBurnsRally_SSE.exe v1.02 versi - Download the latest version from the link shown above (ZIP file). - Unzip the NGPCarMenu-versionTag.zip file to the root folder of existing installation of RBR game (for example *c:\games\richardBurnsRally* ). - Check *plugins\NGPCarMenu.ini.sample* file settings, especially the *RBRCITCarListPath* option. This option should point to the carList.ini file from NGP physics plugin (by default *RBRCIT\carlist\carList.ini* which is a relative path under the RBR installation folder). -- Alternatively you can use EasyRBR tool to setup cars. In that case uncomment the EasyRBRPath NGPCarMenu.ini entry and set path to EasyRBR\easyrbr.ini file. -- When RBR game with this plugin is started for the first time then the plugin will automatically copy the NGPCarMenu.ini.sample file as NGPCarMenu.ini file if it doesn't exist yet. If the NGPCarMenu.ini already exists then the plugin continues to use it and uses default value in all new options. Check the sample file for tips about those new options. This way you can simply unzip a new version on top of the existing NGPCarMenu plugin because you won't loose your existing customized NGPCarMenu.ini values. +- Alternatively you can use EasyRBR tool to setup cars. In that case uncomment the EasyRBRPath option in NGPCarMenu.ini and set path to EasyRBR\easyrbr.ini file. +- When RBR game is started for the first time then the plugin will automatically copy the NGPCarMenu.ini.sample file as NGPCarMenu.ini file if it doesn't exist yet. If the NGPCarMenu.ini already exists then the plugin continues to use it and uses default value in all new options. Check the sample file for tips about those new options. This way you can simply unzip a new version on top of the existing NGPCarMenu plugin because you won't loose your existing customized NGPCarMenu.ini values. - Start *RichardBurnsRally_SSE.exe* game as usual and go to *"Options/Plugins/NGPCarMenu"* in-game menu. - At first you probably don't have any car preview images under *Plugins\NGPCarMenu\preview* folder. Select *"Create car images"* menu command to generate new preview images. Sit down and wait while the plugin creates new preview images from all installed car models. - Use the "Create car images" command when you have installed new custom cars for RBR via RBRCIT or EasyRBR tool. - Now you are ready to race! Go to *"Quick Rally/Multiplayer/Season"* in-game menu and verify that you can see the new information and car preview images in *"Select Car"* menu. +- If you use RBRTM online tournament plugin then enable RBRTM_Integration option. If the car preview image is drawn at wrong location in RBRTM menus then check resolution specific RBRTM_CarPictureRect option in NGPCarMenu.ini file. Set correct rectangle coordinates where the car preview image should be dran (pro tip. Take fullscreen screenshot and use Paint app to see the correct coordinates of the bottom left "empty rectangle area" in Shakedown menu). The NGPCarMenu.ini file has various screen resolution specific options. There are default values for the most common resolutions, but if the resolution you use is not there then you should add new resolution entry and set a value to at least ScreenshotCropping option. This option defines the rectangle area used in a car preview image screenshot. If you are upgrading from an old version then your existing NGPCarMenu.ini file remains as it is and all new INI options use a default value. Refer to NGPCarMenu.ini.sample for more information about new options. @@ -55,7 +59,7 @@ rbr\Plugins\NGPCarMenu.ini options (see the INI file for more details): | ScreenshotReplay | Replay filename the plugin uses to generate preview images. | | ScreenshotFileType | PNG or BMP file format in car preview image files. There is also in-game plugin option to set this value. | | ScreenshotAPIType | 0 or 1. 0=DirectX framebuffer capture while creating preview images. 1=GDI capture. Some Win7 PCs seemed to have issues with DX9 framebuffer captures (wrong colors), so this option 1 (GDI) may help in those scenarios. | -| RBRCITCarListPath | Path to NGP carList.ini configuration file (NGP car specs). If you use RBRCIT tool then the tool has downloaded the file in RBRCIT folder | +| RBRCITCarListPath | Path to NGP carList.ini configuration file (NGP car specs). If you use RBRCIT tool then the tool has downloaded the file in RBRCIT folder. | | EasyRBRPath | Path to EasyRBR.ini configuration file (EasyRBR car manager tool configurations). By default this is commented out, so NGPCarMenu assumes RBRCIT tool is used. By uncommenting this line NGPCarMenu uses custom car information from EasyRBR car manager tool. | | ScreenshotCropping | Cropping rectangle for the screenshot. Make it "big enough but not too big" to fill the bottom part of "Select Car" RBR menu screen (empty string or 0 0 0 0 uses full screen in a car preview images). | | CarSelectLeftBlackBar | Optional rectangle black bar to hide the stock RBR "left frame" image in "Select Car" menu (empty string or 0 0 0 0 disables the black side bar). The first value (left pos) also defines the X-pos where the car preview image is drawn. | @@ -63,6 +67,9 @@ rbr\Plugins\NGPCarMenu.ini options (see the INI file for more details): | Car3DModelInfoPosition | 3D model into textbox X Y position. Empty or missing uses the default position. 320 200 would set both X and Y positions, but having just 320 value would set X position only and Y would be at default position. | | ScreenshotCarPosition | Car position within the replay video while taking the car preview screenshot. *(not yet implemented)* | | ScreenshotCamPosition | Camera position while taking the screenshot. *(not yet implemented)* | +| RBRTM_Integration | 0 or 1. 0=RBRTM integration disabled. 1=RBRTM integration enabled (if the RBRTM plugin is installed and it is V0.88 version) | +| RBRTM_CarPictureRect | Rectangle coordinates where the car preview image is drawn in RBRTM Shakedown and OnlineTournament menus (left top right bottom) | +| RBRTM_CarPictureCropping | Rectangle coordinates of cropping area of the preview image shown in RBRTM menus *(not yet implemented)* | ## Questions and Answers - **Does "Create car image" command overwrite existing car preview images?** No, the plugin uses *"Create only missing images"* option by default. However, you can change this option in the plugin's in-game menu to *"All car images"* option. This option re-creates all car images. @@ -77,6 +84,8 @@ rbr\Plugins\NGPCarMenu.ini options (see the INI file for more details): - **Can I use this plugin with the original RBR car models without NGP plugin?** Hmmm... Can't see a single reason why NGP car models are not used in RBR. Anyway, in that case you don't need this plugin because the original game has all the information needed for original car models. - **What are rbr\plugins\NGPCarMenu\preview\1920x1080 and 1366x768 folders?** This plugin saves new car preview images per resolution (the same preview image may not be the best one for all native resolutions). The *preview* folder has a subfolder which matches the resolution you use (the plugin creates the folder if it is missing). Also, NGPCarMenu.ini file should have a configuration entry block per resolution. - **Why the "Create car image" replay sometimes continues to run as a normal replay video after the last car?** It could happen if the last generated car is actually the same carID used while recording the replay video. In this case you can quit the replay by pressing ESC key. +- **Why the car preview image is in wrong location in RBRTM menus?** The current version of NGPCarMenu has a crappy routine to map RBR in-game coordinates to native screen resolution coordinates. Some resolutions are not handled correctly. To fix the issue tweak RBRTM_CarPictureRect option in NGPCarMenu.ini file and set better coordinates (left top right bottom). +- **Why the car preview image in RBRTM menu has wrong aspect ratio in some resolutions?** The current version of NGPCarMenu has a crappy routine to map RBR in-game coordinates to native screen resolution coordinates. Also, the RBRTM_CarPictureCropping option is not yet implemented which would help to scale portion of the original preview image in correct aspect ratio. - **What is the folder structure used by this plugin?** The DLL and INI plugin files must be in the Plugins directory under the RBR game root folder. Replays RBR folder should have a replay file set in *ScreenshotReplay* option. The file hierarchy is something like this: c:\apps\richardBurnsRally\plugins\NGPCarMenu.dll @@ -93,6 +102,7 @@ Also, some Windows OS setups limit writing new files under c:\program files\ or - Next Generation Physics RBR plugin, NGP. (author: WorkerBee). [NGP Home page](http://www.ly-racing.de/viewtopic.php?t=7878) - FixUp RBR plugin. (author: WorkerBee). [FixUp Home page](http://www.ly-racing.de/viewtopic.php?t=7878) - Pacenote RBR plugin. (author: WorkerBee). [Pacenote Home page](http://www.ly-racing.de/viewtopic.php?t=6848) +- RBRTM Czech Tournament plugin. (author: Wally). [RBRTM Home page](https://rbr.onlineracing.cz/?setlng=eng) - RBR Car Installation Tool, RBRCIT. (author: Zissakos). [RBRCIT Home page](https://github.com/zissakos/RBRCIT) - EasyRBR car and track manager tool. (author: Plankgas/PTD). [EasyRBR Home page](https://www.ptd-3d.com/easyrbr/) diff --git a/misc/NGPCarMenu_PluginMenu.png b/misc/NGPCarMenu_PluginMenu.png index 9521a5a..77a79b1 100644 Binary files a/misc/NGPCarMenu_PluginMenu.png and b/misc/NGPCarMenu_PluginMenu.png differ diff --git a/misc/NGPCarMenu_RBRTMMenu.png b/misc/NGPCarMenu_RBRTMMenu.png new file mode 100644 index 0000000..4091b73 Binary files /dev/null and b/misc/NGPCarMenu_RBRTMMenu.png differ diff --git a/src/D3D9Font/D3DFont.cpp b/src/D3D9Font/D3DFont.cpp index 0d09d53..aabee32 100644 --- a/src/D3D9Font/D3DFont.cpp +++ b/src/D3D9Font/D3DFont.cpp @@ -378,6 +378,7 @@ HRESULT CD3DFont::DrawText(int _sx, int _sy, DWORD dwColor, const WCHAR* strText // Adjust for character spacing auto spaceSize = m_font->GetCharacterSize(m_pd3dDevice, L' '); float startX = sx; + float endX = sx; // Fill vertex buffer FONT2DVERTEX* vertices = NULL; @@ -497,12 +498,23 @@ HRESULT CD3DFont::DrawText(int _sx, int _sy, DWORD dwColor, const WCHAR* strText } sx += w; + + // If this is multiline output then endX will be the width of the longest line + if (sx > endX) + endX = sx; } // Unlock and render the vertex buffer m_pVB->Unlock(); if (trianglesCount > 0) { + // Optionally clear background under the text + if (dwFlags & D3DFONT_CLEARTARGET) + { + D3DRECT rec = { _sx, _sy, static_cast(endX), static_cast(sy) + spaceSize.cy }; + m_pd3dDevice->Clear(1, &rec, D3DCLEAR_TARGET, D3DCOLOR_ARGB(255, 0, 0, 0), 0, 0); + } + for (size_t i = 0; i < textures.size(); i++) { m_pd3dDevice->SetTexture(0, textures[i]); @@ -516,3 +528,9 @@ HRESULT CD3DFont::DrawText(int _sx, int _sy, DWORD dwColor, const WCHAR* strText return S_OK; } + +HRESULT CD3DFont::DrawText(int _sx, int _sy, DWORD dwColor, const CHAR* strText, DWORD dwFlags) +{ + std::wstring wszText = _ToWString(std::string(strText)); + return DrawText(_sx, _sy, dwColor, wszText.c_str(), dwFlags); +} diff --git a/src/D3D9Font/D3DFont.h b/src/D3D9Font/D3DFont.h index 34a8349..1b3f7be 100644 --- a/src/D3D9Font/D3DFont.h +++ b/src/D3D9Font/D3DFont.h @@ -26,6 +26,7 @@ #define D3DFONT_BORDER 0x0010 #define D3DFONT_COLORTABLE 0x0020 +#define D3DFONT_CLEARTARGET 0x0080 // Defined deprecated D3Dx9.h struct because the old DX9 derived code uses these structs typedef struct D3DXVECTOR4 { @@ -269,6 +270,7 @@ class CD3DFont public: // 2D and 3D text drawing functions //HRESULT DrawText(FLOAT x, FLOAT y, DWORD dwColor, const WCHAR* strText, DWORD dwFlags = 0L); + HRESULT DrawText(int x, int y, DWORD dwColor, const CHAR* strText, DWORD dwFlags = 0L); HRESULT DrawText(int x, int y, DWORD dwColor, const WCHAR* strText, DWORD dwFlags = 0L); // Function to get extent of text diff --git a/src/NGPCarMenu.cpp b/src/NGPCarMenu.cpp index 9734946..5d07795 100644 --- a/src/NGPCarMenu.cpp +++ b/src/NGPCarMenu.cpp @@ -57,6 +57,7 @@ CD3DFont* g_pFontCarSpecCustom = nullptr; CNGPCarMenu* g_pRBRPlugin = nullptr; // The one and only RBRPlugin instance +// TODO: Move to RBRAPI.cpp file PRBRGameConfig g_pRBRGameConfig = nullptr; // Various RBR-API struct pointers PRBRGameMode g_pRBRGameMode = nullptr; PRBRGameModeExt g_pRBRGameModeExt = nullptr; @@ -69,7 +70,8 @@ PRBRMapInfo g_pRBRMapInfo = nullptr; PRBRMapSettings g_pRBRMapSettings = nullptr; PRBRGhostCarMovement g_pRBRGhostCarMovement = nullptr; -PRBRMenuSystem g_pRBRMenuSystem = nullptr; +PRBRMenuSystem g_pRBRMenuSystem = nullptr; // Pointer to RBR menu system (all standard menu objects) +PRBRPluginMenuSystem g_pRBRPluginMenuSystem = nullptr; // Pointer to RBR plugin menu system (for some reason Plugins menu is not part of the std menu arrays) WCHAR* g_pOrigCarSpecTitleWeight = nullptr; // The original RBR Weight and Transmission title string values WCHAR* g_pOrigCarSpecTitleTransmission = nullptr; @@ -109,30 +111,38 @@ RBRCarSelectionMenuEntry g_RBRCarSelectionMenuEntry[8] = { // // Menu item command ID and names (custom plugin menu). The ID should match the g_RBRPluginMenu array index (0...n) // -#define C_MENUCMD_RELOAD 0 -#define C_MENUCMD_CREATEOPTION 1 -#define C_MENUCMD_IMAGEOPTION 2 -#define C_MENUCMD_CREATE 3 - -char* g_RBRPluginMenu[4] = { - "RELOAD car images" // Clear cached car images to force re-loading of new images - ,"Create option" // CreateOptions - ,"Image option" // CreateOptions +#define C_MENUCMD_CREATEOPTION 0 +#define C_MENUCMD_IMAGEOPTION 1 +#define C_MENUCMD_RBRTMOPTION 2 +#define C_MENUCMD_RELOAD 3 +#define C_MENUCMD_CREATE 4 + +char* g_NGPCarMenu_PluginMenu[5] = { + "> Create option" // CreateOptions + ,"> Image option" // ImageOptions + ,"> RBRTM integration option" // EnableDisableOptions + ,"RELOAD car images" // Clear cached car images to force re-loading of new images ,"CREATE car images" // Create new car images (all or only missing car iamges) }; // CreateOption menu option names (option values in "Create option") -char* g_RBRPluginMenu_CreateOptions[2] = { +char* g_NGPCarMenu_CreateOptions[2] = { "Only missing car images" ,"All car images" }; // Image output option -char* g_RBRPluginMenu_ImageOptions[2] = { +char* g_NGPCarMenu_ImageOptions[2] = { "PNG" ,"BMP" }; +// RMRTB integration option +char* g_NGPCarMenu_EnableDisableOptions[2] = { + "Disabled" + ,"Enabled" +}; + //----------------------------------------------------------------------------------------------------------------------------- #if USE_DEBUG == 1 @@ -178,6 +188,9 @@ IPlugin* RBR_CreatePlugin( IRBRGame* pGame ) DebugPrint("--------------------------------------------\nRBR_CreatePlugin"); if (g_pRBRPlugin == nullptr) g_pRBRPlugin = new CNGPCarMenu(pGame); + + DebugPrint("NGPCarMenu=%08x", g_pRBRPlugin); + return g_pRBRPlugin; } @@ -214,6 +227,7 @@ CNGPCarMenu::CNGPCarMenu(IRBRGame* pGame) m_screenshotCroppingRectVertexBuffer = nullptr; ZeroMemory(m_carPreviewTexture, sizeof(m_carPreviewTexture)); + ZeroMemory(m_carRBRTMPreviewTexture, sizeof(m_carRBRTMPreviewTexture)); ZeroMemory(&m_screenshotCroppingRect, sizeof(m_screenshotCroppingRect)); ZeroMemory(&m_carSelectLeftBlackBarRect, sizeof(m_carSelectLeftBlackBarRect)); @@ -226,9 +240,15 @@ CNGPCarMenu::CNGPCarMenu(IRBRGame* pGame) m_iMenuSelection = 0; m_iMenuCreateOption = 0; m_iMenuImageOption = 0; + m_iMenuRBRTMOption = 0; m_screenshotAPIType = C_SCREENSHOTAPITYPE_DIRECTX; + m_iRBRTMPluginMenuIdx = 0; // Index (Nth item) to RBRTM plugin in RBR in-game Plugins menu list (0=Not yet initialized, -1=Initialized but RBRTM plugin missing or wrong version) + m_pRBRTMPlugin = nullptr; // Pointer to RBRTM plugin object + m_bRBRTMPluginActive = false; // Is RBRTM plugin currently the active custom frontend plugin + m_iRBRTMCarSelectionType = 0; // If RBRTM is activated then is it at 1=Shakedown or 2=OnlineTournament car selection menu state + RefreshSettingsFromPluginINIFile(); // If EASYRBRPath is not set then assume cars have been setup using RBRCIT car manager tool @@ -271,11 +291,16 @@ CNGPCarMenu::~CNGPCarMenu(void) void CNGPCarMenu::ClearCachedCarPreviewImages() { for (int idx = 0; idx < 8; idx++) + { SAFE_RELEASE(m_carPreviewTexture[idx].pTexture); + SAFE_RELEASE(m_carRBRTMPreviewTexture[idx].pTexture); + } - memset(m_carPreviewTexture, 0, sizeof(m_carPreviewTexture)); + ZeroMemory(m_carPreviewTexture, sizeof(m_carPreviewTexture)); + ZeroMemory(m_carRBRTMPreviewTexture, sizeof(m_carRBRTMPreviewTexture)); } + //------------------------------------------------------------------------------------------------- // Refresh pluging settings from INI file. This method can be called at any time to refresh values even at plugin runtime. // @@ -361,11 +386,11 @@ void CNGPCarMenu::RefreshSettingsFromPluginINIFile() this->m_iMenuImageOption = 0; sTextValue = pluginINIFile.GetValue(L"Default", L"ScreenshotFileType", L""); _Trim(sTextValue); - for (int idx = 0; idx < COUNT_OF_ITEMS(g_RBRPluginMenu_ImageOptions); idx++) + for (int idx = 0; idx < COUNT_OF_ITEMS(g_NGPCarMenu_ImageOptions); idx++) { std::string sOptionValue; std::wstring wsOptionValue; - sOptionValue = g_RBRPluginMenu_ImageOptions[idx]; + sOptionValue = g_NGPCarMenu_ImageOptions[idx]; wsOptionValue = _ToWString(sOptionValue); if (_wcsnicmp(sTextValue.c_str(), wsOptionValue.c_str(), wsOptionValue.length()) == 0) @@ -376,6 +401,32 @@ void CNGPCarMenu::RefreshSettingsFromPluginINIFile() } + // RBRTM integration properties + sTextValue = pluginINIFile.GetValue(L"Default", L"RBRTM_Integration", L"1"); + _Trim(sTextValue); + m_iMenuRBRTMOption = (std::stoi(sTextValue) >=1 ? 1 : 0); + + sTextValue = pluginINIFile.GetValue(szResolutionText, L"RBRTM_CarPictureRect", L""); + _StringToRect(sTextValue, &this->m_carRBRTMPictureRect); + if (g_pRBRIDirect3DDevice9 != nullptr && m_carRBRTMPictureRect.left == 0 && m_carRBRTMPictureRect.top == 0 && m_carRBRTMPictureRect.right == 0 && m_carRBRTMPictureRect.bottom == 0) + { + // Default rectangle area of RBRTM car preview pic + RBRAPI_MapRBRPointToScreenPoint((float)m_carSelectLeftBlackBarRect.left, 248.0f, (int*)&m_carRBRTMPictureRect.left, (int*)&m_carRBRTMPictureRect.top); + RBRAPI_MapRBRPointToScreenPoint(452.0f, 462.0f, (int*)&m_carRBRTMPictureRect.right, (int*)&m_carRBRTMPictureRect.bottom); + } + + sTextValue = pluginINIFile.GetValue(szResolutionText, L"RBRTM_CarPictureCropping", L""); + _StringToRect(sTextValue, &this->m_carRBRTMPictureCropping); + +/* sTextValue = pluginINIFile.GetValue(szResolutionText, L"RBRTM_Car3DModelInfoPosition", L""); + _StringToPoint(sTextValue, &this->m_carRBRTM3DModelInfoPosition); + if (g_pRBRIDirect3DDevice9 != nullptr && m_carRBRTM3DModelInfoPosition.x == 0 && m_carRBRTM3DModelInfoPosition.y == 0) + { + // Default X,Y position of car spec info textbox in RBRTM screens + RBRAPI_MapRBRPointToScreenPoint((float)m_carRBRTMPictureRect.right + 10, (float)m_carRBRTMPictureRect.bottom - (6 * g_pFontCarSpecCustom->GetTextHeight()), (int*)&m_carRBRTM3DModelInfoPosition.x, (int*)&m_carRBRTM3DModelInfoPosition.y); + } +*/ + // // Set status text msg (or if the status1 is already set then it must be an error msg set by this method) // @@ -404,10 +455,12 @@ void CNGPCarMenu::SaveSettingsToPluginINIFile() sIniFileName = CNGPCarMenu::m_sRBRRootDir + "\\Plugins\\NGPCarMenu.ini"; pluginINIFile.LoadFile(sIniFileName.c_str()); - sOptionValue = g_RBRPluginMenu_ImageOptions[this->m_iMenuImageOption]; + sOptionValue = g_NGPCarMenu_ImageOptions[this->m_iMenuImageOption]; wsOptionValue = _ToWString(sOptionValue); - pluginINIFile.SetValue(L"Default", L"ScreenshotFileType", wsOptionValue.c_str()); + + pluginINIFile.SetValue(L"Default", L"RBRTM_Integration", std::to_wstring(this->m_iMenuRBRTMOption).c_str()); + pluginINIFile.SaveFile(sIniFileName.c_str()); } catch (...) @@ -418,6 +471,51 @@ void CNGPCarMenu::SaveSettingsToPluginINIFile() } +//------------------------------------------------------------------------------------------------- +// Initialize RBRTM plugin integration +// +bool CNGPCarMenu::InitRBRTMPluginIntegration() +{ + // Try to lookup RBRTM plugin pointer only when all plugins have and RBR DX9 device is already initialized (this RefreshSettingsFromPluginINIFile method is called again when RBR DX9 is initialized) + if (m_iMenuRBRTMOption > 0 && m_iRBRTMPluginMenuIdx == 0 && g_pRBRIDirect3DDevice9 != nullptr) + { + if (g_pRBRPluginMenuSystem == nullptr) g_pRBRPluginMenuSystem = (PRBRPluginMenuSystem) * (DWORD*)(0x0165FC48); + + // Check that RBRTM plugin is installed and loaded in RBR and it is the correct version + if (g_pRBRPluginMenuSystem != nullptr && g_pRBRPluginMenuSystem->pluginsMenuObj != nullptr && g_pRBRPluginMenuSystem->pluginsMenuObj->pItemObj != nullptr) + { + for (int idx = g_pRBRPluginMenuSystem->pluginsMenuObj->firstSelectableItemIdx; idx < g_pRBRPluginMenuSystem->pluginsMenuObj->numOfItems - 1; idx++) + { + if (g_pRBRPluginMenuSystem->pluginsMenuObj->pItemObj[idx] != nullptr && strncmp(((PRBRPluginMenuSystemItemObj)g_pRBRPluginMenuSystem->pluginsMenuObj->pItemObj[idx])->szPluginName, C_RBRTM_PLUGIN_NAME, sizeof(C_RBRTM_PLUGIN_NAME) - 1) == 0) + { + // Check version before accepting the RBRTM plugin + std::string sRBRVersion = GetFileVersionInformationAsString(m_sRBRRootDirW + L"\\Plugins\\RBRTM.DLL"); + LogPrint("RBRTM plugin version %s detected", sRBRVersion.c_str()); + + if (sRBRVersion.compare("0.8.8.0") == 0) + m_iRBRTMPluginMenuIdx = idx; // Plugins menu index to RBRTM plugin (ie. RBR may load plugins in random order, so certain plugin is not always in the same position in the Plugins menu list) + + break; + } + } + } + + if (m_iRBRTMPluginMenuIdx > 0 && ::ReadOpCodePtr((LPVOID)0x1597F128, (LPVOID*)&m_pRBRTMPlugin) && m_pRBRTMPlugin != nullptr) + { + LogPrint("RBRTM plugin integration enabled"); + } + else + { + LogPrint("RBRTM plugin integration disabled. The plugin is not loaded or the version was not the expected RBRTM v0.88"); + m_pRBRTMPlugin = nullptr; + m_iRBRTMPluginMenuIdx = -1; + } + } + + return m_pRBRTMPlugin != nullptr; +} + + //------------------------------------------------------------------------------------------------- // RBRCIT support: Init car spec data by reading NGP plugin and RBRCIT config files // - Step1: rbr\Physics\\ RBR folder and find the file without extensions and containing keywords revision/specification date/3D model @@ -1028,7 +1126,7 @@ int CNGPCarMenu::GetNextScreenshotCarID(int currentCarID) if (m_iMenuCreateOption == 0) { std::string imgExtension = "."; - imgExtension = imgExtension + g_RBRPluginMenu_ImageOptions[m_iMenuImageOption]; + imgExtension = imgExtension + g_NGPCarMenu_ImageOptions[m_iMenuImageOption]; _ToLowerCase(imgExtension); // If the output PNG preview image file already exists then don't re-generate the screenshot (menu option "Only missing car images") @@ -1133,6 +1231,29 @@ bool CNGPCarMenu::PrepareScreenshotReplayFile(int carID) } +//------------------------------------------------------------------------------------------------ +// Initialize car preview image DX9 texture and vertex objects +// +bool CNGPCarMenu::ReadCarPreviewImageFromFile(int selectedCarIdx, float x, float y, float cx, float cy, IMAGE_TEXTURE* pOutImageTexture) +{ + // TODO. Read image size and re-scale and center the image if it is not in native resolution folder + + std::string imgExtension = "."; + imgExtension = imgExtension + g_NGPCarMenu_ImageOptions[this->m_iMenuImageOption]; // .PNG or .BMP img file format + _ToLowerCase(imgExtension); + + HRESULT hResult = D3D9CreateRectangleVertexTexBufferFromFile(g_pRBRIDirect3DDevice9, + this->m_screenshotPath + L"\\" + g_RBRCarSelectionMenuEntry[selectedCarIdx].wszCarModel + _ToWString(imgExtension), + x, y, cx, cy, + pOutImageTexture); + + // Image not available. Do not try to re-load it again (set cx=-1 to indicate that the image loading failed, so no need to try to re-load it in every frame even when texture is null) + if (!SUCCEEDED(hResult)) pOutImageTexture->imgSize.cx = -1; + + return pOutImageTexture->imgSize.cx != -1; +} + + //------------------------------------------------------------------------------------------------ // const char* CNGPCarMenu::GetName(void) @@ -1142,6 +1263,8 @@ const char* CNGPCarMenu::GetName(void) // Re-route RBR DXEndScene function to our custom function if (g_bRBRHooksInitialized == FALSE && m_PluginState == T_PLUGINSTATE::PLUGINSTATE_UNINITIALIZED) { + DebugPrint("CNGPCarMenu.GetName. First time initialization of RBR hooks"); + try { std::string sReplayTemplateFileName; @@ -1171,38 +1294,39 @@ const char* CNGPCarMenu::GetName(void) D3DDEVICE_CREATION_PARAMETERS d3dCreationParameters; g_pRBRIDirect3DDevice9->GetCreationParameters(&d3dCreationParameters); g_hRBRWnd = d3dCreationParameters.hFocusWindow; - RBRAPI_RefreshWndRect(); - // Pointer 0x493980 -> rbrHwnd? Can it be used to re-route WM messages to our own windows handler and this way to "listen" RBR key presses? + // Pointer 0x493980 -> rbrHwnd? Can it be used to re-route WM messages to our own windows handler and this way to "listen" RBR key presses if this plugin needs key controls? - RefreshSettingsFromPluginINIFile(); - - // Initialize D3D9 compatible font classes -#if USE_DEBUG == 1 - g_pFontDebug = new CD3DFont(L"Courier New", 11, 0); - g_pFontDebug->InitDeviceObjects(g_pRBRIDirect3DDevice9); - g_pFontDebug->RestoreDeviceObjects(); -#endif + // Init font to draw custom car spec info text (3D model and custom livery text) int iFontSize = 12; if (g_rectRBRWndClient.bottom < 600) iFontSize = 7; else if (g_rectRBRWndClient.bottom < 768) iFontSize = 9; g_pFontCarSpecCustom = new CD3DFont(L"Trebuchet MS", iFontSize, 0); g_pFontCarSpecCustom->InitDeviceObjects(g_pRBRIDirect3DDevice9); - g_pFontCarSpecCustom->RestoreDeviceObjects(); + g_pFontCarSpecCustom->RestoreDeviceObjects(); - if (g_pRBRGameConfig == NULL) g_pRBRGameConfig = (PRBRGameConfig) *(DWORD*)(0x007EAC48); - if (g_pRBRGameMode == NULL) g_pRBRGameMode = (PRBRGameMode) * (DWORD*)(0x007EAC48); - if (g_pRBRGameModeExt == NULL) g_pRBRGameModeExt = (PRBRGameModeExt) * (DWORD*)(0x00893634); + // Read and refresh INI values now when RBR DX9 object is initialized + RefreshSettingsFromPluginINIFile(); - if (g_pRBRCarInfo == NULL) g_pRBRCarInfo = (PRBRCarInfo) *(DWORD*)(0x0165FC68); - if (g_pRBRCarControls == NULL) g_pRBRCarControls = (PRBRCarControls) *(DWORD*)(0x007EAC48); // +0x738 + 0x5C; +#if USE_DEBUG == 1 + g_pFontDebug = new CD3DFont(L"Courier New", 11, 0); + g_pFontDebug->InitDeviceObjects(g_pRBRIDirect3DDevice9); + g_pFontDebug->RestoreDeviceObjects(); +#endif + + if (g_pRBRGameConfig == nullptr) g_pRBRGameConfig = (PRBRGameConfig) *(DWORD*)(0x007EAC48); + if (g_pRBRGameMode == nullptr) g_pRBRGameMode = (PRBRGameMode) * (DWORD*)(0x007EAC48); + if (g_pRBRGameModeExt == nullptr) g_pRBRGameModeExt = (PRBRGameModeExt) * (DWORD*)(0x00893634); + + if (g_pRBRCarInfo == nullptr) g_pRBRCarInfo = (PRBRCarInfo) *(DWORD*)(0x0165FC68); + if (g_pRBRCarControls == nullptr) g_pRBRCarControls = (PRBRCarControls) *(DWORD*)(0x007EAC48); // +0x738 + 0x5C; - //if (pRBRCarMovement == NULL) pRBRCarMovement = (PRBRCarMovement) *(DWORD*)(0x008EF660); // This pointer is valid only when replay or stage is starting - if (g_pRBRGhostCarMovement == NULL) g_pRBRGhostCarMovement = (PRBRGhostCarMovement)(DWORD*)(0x00893060); + //if (pRBRCarMovement == nullptr) pRBRCarMovement = (PRBRCarMovement) *(DWORD*)(0x008EF660); // This pointer is valid only when replay or stage is starting + if (g_pRBRGhostCarMovement == nullptr) g_pRBRGhostCarMovement = (PRBRGhostCarMovement)(DWORD*)(0x00893060); - if (g_pRBRMenuSystem == NULL) g_pRBRMenuSystem = (PRBRMenuSystem) *(DWORD*)(0x0165FA48); + if (g_pRBRMenuSystem == nullptr) g_pRBRMenuSystem = (PRBRMenuSystem) *(DWORD*)(0x0165FA48); // Fixed location to mapSettings struct (ie. not a pointer reference). g_pRBRMapSettings = (PRBRMapSettings)(0x1660800); @@ -1233,6 +1357,7 @@ const char* CNGPCarMenu::GetName(void) } } + // // Ready to rock! Re-route and store the original function address for later use. At this point all variables used in custom Dx0 functions should be initialized already // @@ -1303,14 +1428,16 @@ void CNGPCarMenu::DrawFrontEndPage(void) m_pGame->DrawSelection(0.0f, 68.0f + (static_cast< float >(m_iMenuSelection) * 21.0f), 360.0f); m_pGame->SetMenuColor(IRBRGame::MENU_TEXT); - for (unsigned int i = 0; i < COUNT_OF_ITEMS(g_RBRPluginMenu); ++i) + for (unsigned int i = 0; i < COUNT_OF_ITEMS(g_NGPCarMenu_PluginMenu); ++i) { if (i == C_MENUCMD_CREATEOPTION) - sprintf_s(szTextBuf, sizeof(szTextBuf), "%s: %s", g_RBRPluginMenu[i], g_RBRPluginMenu_CreateOptions[m_iMenuCreateOption]); + sprintf_s(szTextBuf, sizeof(szTextBuf), "%s: %s", g_NGPCarMenu_PluginMenu[i], g_NGPCarMenu_CreateOptions[m_iMenuCreateOption]); else if (i == C_MENUCMD_IMAGEOPTION) - sprintf_s(szTextBuf, sizeof(szTextBuf), "%s: %s", g_RBRPluginMenu[i], g_RBRPluginMenu_ImageOptions[m_iMenuImageOption]); + sprintf_s(szTextBuf, sizeof(szTextBuf), "%s: %s", g_NGPCarMenu_PluginMenu[i], g_NGPCarMenu_ImageOptions[m_iMenuImageOption]); + else if (i == C_MENUCMD_RBRTMOPTION) + sprintf_s(szTextBuf, sizeof(szTextBuf), "%s: %s", g_NGPCarMenu_PluginMenu[i], g_NGPCarMenu_EnableDisableOptions[m_iMenuRBRTMOption]); else - sprintf_s(szTextBuf, sizeof(szTextBuf), "%s", g_RBRPluginMenu[i]); + sprintf_s(szTextBuf, sizeof(szTextBuf), "%s", g_NGPCarMenu_PluginMenu[i]); m_pGame->WriteText(65.0f, 70.0f + (static_cast< float >(i) * 21.0f), szTextBuf); } @@ -1318,25 +1445,34 @@ void CNGPCarMenu::DrawFrontEndPage(void) if (!m_sMenuStatusText1.empty()) { m_pGame->SetFont(IRBRGame::FONT_SMALL); - m_pGame->WriteText(10.0f, 70.0f + (static_cast(COUNT_OF_ITEMS(g_RBRPluginMenu) + 3) * 21.0f), m_sMenuStatusText1.c_str()); + m_pGame->WriteText(10.0f, 70.0f + (static_cast(COUNT_OF_ITEMS(g_NGPCarMenu_PluginMenu) + 3) * 21.0f), m_sMenuStatusText1.c_str()); } if (!m_sMenuStatusText2.empty()) { m_pGame->SetFont(IRBRGame::FONT_SMALL); - m_pGame->WriteText(10.0f, 70.0f + (static_cast(COUNT_OF_ITEMS(g_RBRPluginMenu) + 4) * 21.0f), m_sMenuStatusText2.c_str()); + m_pGame->WriteText(10.0f, 70.0f + (static_cast(COUNT_OF_ITEMS(g_NGPCarMenu_PluginMenu) + 4) * 21.0f), m_sMenuStatusText2.c_str()); } if (!m_sMenuStatusText3.empty()) { m_pGame->SetFont(IRBRGame::FONT_SMALL); - m_pGame->WriteText(10.0f, 70.0f + (static_cast(COUNT_OF_ITEMS(g_RBRPluginMenu) + 5) * 21.0f), m_sMenuStatusText3.c_str()); + m_pGame->WriteText(10.0f, 70.0f + (static_cast(COUNT_OF_ITEMS(g_NGPCarMenu_PluginMenu) + 5) * 21.0f), m_sMenuStatusText3.c_str()); } } //------------------------------------------------------------------------------------------------ // +//#if USE_DEBUG == 1 +//typedef IPlugin* ( *tRBR_CreatePlugin)(IRBRGame* pGame); +//#endif + +#define DO_MENUSELECTION_LEFTRIGHT(OptionID, OptionValueVariable, OptionArray) \ + if (m_iMenuSelection == OptionID && bLeft && (--OptionValueVariable) < 0) OptionValueVariable = 0; \ + else if (m_iMenuSelection == OptionID && bRight && (++OptionValueVariable) >= COUNT_OF_ITEMS(OptionArray)) OptionValueVariable = COUNT_OF_ITEMS(OptionArray)-1 + + void CNGPCarMenu::HandleFrontEndEvents(char txtKeyboard, bool bUp, bool bDown, bool bLeft, bool bRight, bool bSelect) { // Clear the previous status text when menu selection changes @@ -1404,30 +1540,24 @@ void CNGPCarMenu::HandleFrontEndEvents(char txtKeyboard, bool bUp, bool bDown, b //m_iMenuSelection = COUNT_OF_ITEMS(g_RBRPluginMenu) - 1; // Wrap around logic m_iMenuSelection = 0; // No wrapping logic - if (bDown && (++m_iMenuSelection) >= COUNT_OF_ITEMS(g_RBRPluginMenu)) + if (bDown && (++m_iMenuSelection) >= COUNT_OF_ITEMS(g_NGPCarMenu_PluginMenu)) //m_iMenuSelection = 0; // Wrap around logic - m_iMenuSelection = COUNT_OF_ITEMS(g_RBRPluginMenu) - 1; + m_iMenuSelection = COUNT_OF_ITEMS(g_NGPCarMenu_PluginMenu) - 1; // // Menu options changed in the current menu line. Options don't wrap around. // Note! Not all menu lines have any additional options. // - if (m_iMenuSelection == C_MENUCMD_CREATEOPTION && bLeft && (--m_iMenuCreateOption) < 0) - m_iMenuCreateOption = 0; - - if (m_iMenuSelection == C_MENUCMD_CREATEOPTION && bRight && (++m_iMenuCreateOption) >= COUNT_OF_ITEMS(g_RBRPluginMenu_CreateOptions)) - m_iMenuCreateOption = COUNT_OF_ITEMS(g_RBRPluginMenu_CreateOptions) - 1; - + DO_MENUSELECTION_LEFTRIGHT(C_MENUCMD_CREATEOPTION, m_iMenuCreateOption, g_NGPCarMenu_CreateOptions); int iPrevMenuImageOptionValue = m_iMenuImageOption; - if (m_iMenuSelection == C_MENUCMD_IMAGEOPTION && bLeft && (--m_iMenuImageOption) < 0) - m_iMenuImageOption = 0; + DO_MENUSELECTION_LEFTRIGHT(C_MENUCMD_IMAGEOPTION, m_iMenuImageOption, g_NGPCarMenu_ImageOptions); - if (m_iMenuSelection == C_MENUCMD_IMAGEOPTION && bRight && (++m_iMenuImageOption) >= COUNT_OF_ITEMS(g_RBRPluginMenu_ImageOptions)) - m_iMenuImageOption = COUNT_OF_ITEMS(g_RBRPluginMenu_ImageOptions) - 1; + int iPrevMenuRMRTMOptionValue = m_iMenuRBRTMOption; + DO_MENUSELECTION_LEFTRIGHT(C_MENUCMD_RBRTMOPTION, m_iMenuRBRTMOption, g_NGPCarMenu_EnableDisableOptions); - if(iPrevMenuImageOptionValue != m_iMenuImageOption) + if(iPrevMenuImageOptionValue != m_iMenuImageOption || iPrevMenuRMRTMOptionValue != m_iMenuRBRTMOption) SaveSettingsToPluginINIFile(); } @@ -1462,8 +1592,8 @@ void CNGPCarMenu::HandleResults(float fCheckPoint1, float fCheckPoint2, float fF // void CNGPCarMenu::StageStarted(int iMap, const char* ptxtPlayerName, bool bWasFalseStart) { - g_pRBRMapInfo = (PRBRMapInfo) *(DWORD*)(0x1659184); - g_pRBRCarMovement = (PRBRCarMovement) *(DWORD*)(0x008EF660); + //g_pRBRMapInfo = (PRBRMapInfo) *(DWORD*)(0x1659184); + //g_pRBRCarMovement = (PRBRCarMovement) *(DWORD*)(0x008EF660); } @@ -1667,137 +1797,226 @@ HRESULT __fastcall CustomRBRDirectXEndScene(void* objPointer) if (!g_bRBRHooksInitialized) return S_OK; - if (g_pRBRGameMode->gameMode == 03 - && (g_pRBRMenuSystem->currentMenuObj == g_pRBRMenuSystem->menuObj[RBRMENUIDX_QUICKRALLY_CARS] + if (g_pRBRGameMode->gameMode == 03) + { + if (g_pRBRMenuSystem->currentMenuObj == g_pRBRMenuSystem->menuObj[RBRMENUIDX_QUICKRALLY_CARS] || g_pRBRMenuSystem->currentMenuObj == g_pRBRMenuSystem->menuObj[RBRMENUIDX_MULTIPLAYER_CARS_P1] || g_pRBRMenuSystem->currentMenuObj == g_pRBRMenuSystem->menuObj[RBRMENUIDX_MULTIPLAYER_CARS_P2] || g_pRBRMenuSystem->currentMenuObj == g_pRBRMenuSystem->menuObj[RBRMENUIDX_MULTIPLAYER_CARS_P3] || g_pRBRMenuSystem->currentMenuObj == g_pRBRMenuSystem->menuObj[RBRMENUIDX_MULTIPLAYER_CARS_P4] || g_pRBRMenuSystem->currentMenuObj == g_pRBRMenuSystem->menuObj[RBRMENUIDX_RBRCHALLENGE_CARS] ) - ) - { - // Show custom car details in "SelectCar" menu - - int selectedCarIdx = g_pRBRMenuSystem->currentMenuObj->selectedItemIdx - g_pRBRMenuSystem->currentMenuObj->firstSelectableItemIdx; - - if (selectedCarIdx >= 0 && selectedCarIdx <= 7) { - float rbrPosX; - int posX; - int posY; - int iFontHeight; + // Show custom car details in "SelectCar" menu - if (g_pRBRPlugin->m_carPreviewTexture[selectedCarIdx].pTexture == nullptr && g_pRBRPlugin->m_carPreviewTexture[selectedCarIdx].imgSize.cx >= 0 && g_RBRCarSelectionMenuEntry[selectedCarIdx].wszCarModel[0] != '\0') + int selectedCarIdx = g_pRBRMenuSystem->currentMenuObj->selectedItemIdx - g_pRBRMenuSystem->currentMenuObj->firstSelectableItemIdx; + + if (selectedCarIdx >= 0 && selectedCarIdx <= 7) { - // Car preview image is not yet read from a file and cached as D3D9 texture in this plugin. Do it now. - float posYf; - RBRAPI_MapRBRPointToScreenPoint(0, g_pRBRMenuSystem->menuImagePosY-1, nullptr, &posYf); + float rbrPosX; + int posX; + int posY; + int iFontHeight; - // TODO. Read image size and re-scale and center the image if it is not in native resolution folder + if (g_pRBRPlugin->m_carPreviewTexture[selectedCarIdx].pTexture == nullptr && g_pRBRPlugin->m_carPreviewTexture[selectedCarIdx].imgSize.cx >= 0 && g_RBRCarSelectionMenuEntry[selectedCarIdx].wszCarModel[0] != '\0') + { + float posYf; + RBRAPI_MapRBRPointToScreenPoint(0.0f, g_pRBRMenuSystem->menuImagePosY - 1.0f, nullptr, &posYf); - std::string imgExtension = "."; - imgExtension = imgExtension + g_RBRPluginMenu_ImageOptions[g_pRBRPlugin->m_iMenuImageOption]; // .PNG or .BMP img file format - _ToLowerCase(imgExtension); + g_pRBRPlugin->ReadCarPreviewImageFromFile(selectedCarIdx, (float)g_pRBRPlugin->m_carSelectLeftBlackBarRect.left, posYf, 0, 0, &g_pRBRPlugin->m_carPreviewTexture[selectedCarIdx]); + } - HRESULT hResult = D3D9CreateRectangleVertexTexBufferFromFile(g_pRBRIDirect3DDevice9, - g_pRBRPlugin->m_screenshotPath + L"\\" + g_RBRCarSelectionMenuEntry[selectedCarIdx].wszCarModel + _ToWString(imgExtension), - (float)g_pRBRPlugin->m_carSelectLeftBlackBarRect.left, posYf, 0, 0, - &g_pRBRPlugin->m_carPreviewTexture[selectedCarIdx]); + // If the car preview image is successfully initialized (imgSize.cx >= 0) and texture (=image) is prepared then draw it on the screen + if (g_pRBRPlugin->m_carPreviewTexture[selectedCarIdx].imgSize.cx >= 0 && g_pRBRPlugin->m_carPreviewTexture[selectedCarIdx].pTexture != nullptr) + { + D3DRECT rec; + + // Draw black side bars (if set in INI file) + rec.x1 = g_pRBRPlugin->m_carSelectLeftBlackBarRect.left; + rec.y1 = g_pRBRPlugin->m_carSelectLeftBlackBarRect.top; + rec.x2 = g_pRBRPlugin->m_carSelectLeftBlackBarRect.right; + rec.y2 = g_pRBRPlugin->m_carSelectLeftBlackBarRect.bottom; + g_pRBRIDirect3DDevice9->Clear(1, &rec, D3DCLEAR_TARGET, D3DCOLOR_ARGB(255, 0, 0, 0), 0, 0); + + rec.x1 = g_pRBRPlugin->m_carSelectRightBlackBarRect.left; + rec.y1 = g_pRBRPlugin->m_carSelectRightBlackBarRect.top; + rec.x2 = g_pRBRPlugin->m_carSelectRightBlackBarRect.right; + rec.y2 = g_pRBRPlugin->m_carSelectRightBlackBarRect.bottom; + g_pRBRIDirect3DDevice9->Clear(1, &rec, D3DCLEAR_TARGET, D3DCOLOR_ARGB(255, 0, 0, 0), 0, 0); + + // Draw car preview image + D3D9DrawVertexTex2D(g_pRBRIDirect3DDevice9, g_pRBRPlugin->m_carPreviewTexture[selectedCarIdx].pTexture, g_pRBRPlugin->m_carPreviewTexture[selectedCarIdx].vertexes2D); + } - // Image not available. Do not try to re-load it again (set cx=-1 to indicate that the image loading failed, so no need to try to re-load it in every frame even when texture is null) - if (!SUCCEEDED(hResult)) g_pRBRPlugin->m_carPreviewTexture[selectedCarIdx].imgSize.cx = -1; - } - if (g_pRBRPlugin->m_carPreviewTexture[selectedCarIdx].imgSize.cx >= 0 && g_pRBRPlugin->m_carPreviewTexture[selectedCarIdx].pTexture != nullptr) - { - D3DRECT rec; - - // Draw black side bars (if set in INI file) - rec.x1 = g_pRBRPlugin->m_carSelectLeftBlackBarRect.left; - rec.y1 = g_pRBRPlugin->m_carSelectLeftBlackBarRect.top; - rec.x2 = g_pRBRPlugin->m_carSelectLeftBlackBarRect.right; - rec.y2 = g_pRBRPlugin->m_carSelectLeftBlackBarRect.bottom; - g_pRBRIDirect3DDevice9->Clear(1, &rec, D3DCLEAR_TARGET, D3DCOLOR_ARGB(255, 0, 0, 0), 0, 0); - - rec.x1 = g_pRBRPlugin->m_carSelectRightBlackBarRect.left; - rec.y1 = g_pRBRPlugin->m_carSelectRightBlackBarRect.top; - rec.x2 = g_pRBRPlugin->m_carSelectRightBlackBarRect.right; - rec.y2 = g_pRBRPlugin->m_carSelectRightBlackBarRect.bottom; - g_pRBRIDirect3DDevice9->Clear(1, &rec, D3DCLEAR_TARGET, D3DCOLOR_ARGB(255, 0, 0, 0), 0, 0); - - // Draw car preview image - D3D9DrawVertexTex2D(g_pRBRIDirect3DDevice9, g_pRBRPlugin->m_carPreviewTexture[selectedCarIdx].pTexture, g_pRBRPlugin->m_carPreviewTexture[selectedCarIdx].vertexes2D); - } + // + // Car 3D Model info textbo + // + + // X pos of additional custom car spec data scaled per resolution + // TODO. Try to find better routine to handle in-game vs runtime screen resolution re-mapping. The current solution is veeeeery clumsy + // + switch (g_rectRBRWndClient.right) + { + case 640: + case 800: + case 1024: rbrPosX = 218; break; + + case 1440: + case 1680: rbrPosX = 235; break; + + case 1280: + case 1360: + case 1366: + case 1920: rbrPosX = 245; break; + + default: rbrPosX = -1; break; // Do not try to re-scale automatically. Use brute-force "half of RBR wnd width" + } + + iFontHeight = g_pFontCarSpecCustom->GetTextHeight(); + // TODO: More clever and exact re-scaler function to map game resolution to screen resolution (now game position is not always mapped correctly to screen position) + RBRAPI_MapRBRPointToScreenPoint(rbrPosX, g_pRBRMenuSystem->menuImagePosY, &posX, &posY); + posY -= 5 * iFontHeight; + if (g_pRBRPlugin->m_car3DModelInfoPosition.x != 0) + posX = g_pRBRPlugin->m_car3DModelInfoPosition.x; // Custom X-position + else if (rbrPosX == -1) + posX = ((g_rectRBRWndClient.right - g_rectRBRWndClient.left) / 2) - 50; // Default brute-force "center of the horizontal screen line" + + if (g_pRBRPlugin->m_car3DModelInfoPosition.y != 0) + posY = g_pRBRPlugin->m_car3DModelInfoPosition.y; // Custom Y-position + + PRBRCarSelectionMenuEntry pCarSelectionMenuEntry = &g_RBRCarSelectionMenuEntry[selectedCarIdx]; + + // Printout custom carSpec information (if the text line/value is not null string) + int iCarSpecPrintRow = 0; + if (pCarSelectionMenuEntry->wszCarPhysicsRevision[0] != L'\0') + g_pFontCarSpecCustom->DrawText(posX, (iCarSpecPrintRow++) * iFontHeight + posY, C_CARSPECTEXT_COLOR, pCarSelectionMenuEntry->wszCarPhysicsRevision, 0); + + if (pCarSelectionMenuEntry->wszCarPhysicsSpecYear[0] != L'\0') + g_pFontCarSpecCustom->DrawText(posX, (iCarSpecPrintRow++) * iFontHeight + posY, C_CARSPECTEXT_COLOR, pCarSelectionMenuEntry->wszCarPhysicsSpecYear, 0); + + if (pCarSelectionMenuEntry->wszCarPhysics3DModel[0] != L'\0') + g_pFontCarSpecCustom->DrawText(posX, (iCarSpecPrintRow++) * iFontHeight + posY, C_CARSPECTEXT_COLOR, pCarSelectionMenuEntry->wszCarPhysics3DModel, 0); + + if (pCarSelectionMenuEntry->wszCarPhysicsLivery[0] != L'\0') + g_pFontCarSpecCustom->DrawText(posX, (iCarSpecPrintRow++) * iFontHeight + posY, C_CARSPECTEXT_COLOR, pCarSelectionMenuEntry->wszCarPhysicsLivery, 0); + + if (pCarSelectionMenuEntry->wszCarPhysicsCustomTxt[0] != L'\0') + g_pFontCarSpecCustom->DrawText(posX, (iCarSpecPrintRow++) * iFontHeight + posY, C_CARSPECTEXT_COLOR, pCarSelectionMenuEntry->wszCarPhysicsCustomTxt, 0); + } + } + else if(g_pRBRPlugin->m_iMenuRBRTMOption == 1) + { // - // Car 3D Model info textbo + // RBRTM integration enabled. Check if RMRTB is in "SelectCar" menu in Shakedown or OnlineTournament menus // - // X pos of additional custom car spec data scaled per resolution - switch (g_rectRBRWndClient.right) + + bool bRBRTMCarSelectionMenu = false; + + if (g_pRBRPlugin->m_pRBRTMPlugin != nullptr && g_pRBRPluginMenuSystem != nullptr && g_pRBRPlugin->m_pRBRTMPlugin->pCurrentRBRTMMenuObj != nullptr) { - case 640: - case 800: - case 1024: rbrPosX = 218; break; + if (!g_pRBRPlugin->m_bRBRTMPluginActive && g_pRBRMenuSystem->currentMenuObj == g_pRBRPluginMenuSystem->customPluginMenuObj && g_pRBRPluginMenuSystem->pluginsMenuObj->selectedItemIdx == g_pRBRPlugin->m_iRBRTMPluginMenuIdx) + // RBRTM plugin activated via RBR Plugins in-game menu + g_pRBRPlugin->m_bRBRTMPluginActive = true; + else if (g_pRBRPlugin->m_bRBRTMPluginActive && (g_pRBRMenuSystem->currentMenuObj == g_pRBRPluginMenuSystem->pluginsMenuObj || g_pRBRMenuSystem->currentMenuObj == g_pRBRPluginMenuSystem->optionsMenuObj)) + // Menu is back to RBR "Plugins" or "Options" menu list, so RBRTM cannot be the foreground plugin anymore + g_pRBRPlugin->m_bRBRTMPluginActive = false; + + // If the active custom plugin is RBRTM and RBR shows a custom plugin menuObj and the internal RBRTM menu is the Shakedown "car selection" menuID then prepare to render a car preview image + if (g_pRBRPlugin->m_bRBRTMPluginActive && g_pRBRPluginMenuSystem->customPluginMenuObj == g_pRBRMenuSystem->currentMenuObj && g_pRBRPlugin->m_pRBRTMPlugin->pCurrentRBRTMMenuObj != nullptr) + { + switch ((DWORD)*g_pRBRPlugin->m_pRBRTMPlugin->pCurrentRBRTMMenuObj) + { + case 0x15978614: bRBRTMCarSelectionMenu = true; break; // RBRTM car selection screen (either below Shakedown or OnlineTournament menu tree) - case 1440: - case 1680: rbrPosX = 235; break; + case 0x159785F8: g_pRBRPlugin->m_iRBRTMCarSelectionType = 0; break; // Back to RBRTM main menu, so the RBRTM menu is no longer below Shakedown or OnlineTournament menu tree - case 1280: - case 1360: - case 1366: - case 1920: rbrPosX = 245; break; + case 0x159786F8: + case 0x15978684: g_pRBRPlugin->m_iRBRTMCarSelectionType = 1; break; // RBRTM OnlineTournament menu tree - default: rbrPosX = -1; break; // Do not try to re-scale automatically. Use brute-force "half of RBR wnd width" - } + case 0x15978154: + case 0x159786A0: g_pRBRPlugin->m_iRBRTMCarSelectionType = 2; break; // RBRTM Shakedown menu tree + } - iFontHeight = g_pFontCarSpecCustom->GetTextHeight(); + if (g_pRBRPlugin->m_pRBRTMPlugin->pRBRTMStageOptions1 == nullptr || g_pRBRPlugin->m_pRBRTMPlugin->pRBRTMStageOptions1->selectedCarID < 0 || g_pRBRPlugin->m_pRBRTMPlugin->pRBRTMStageOptions1->selectedCarID > 7) + bRBRTMCarSelectionMenu = false; + } + + if (bRBRTMCarSelectionMenu && (g_pRBRPlugin->m_iRBRTMCarSelectionType == 2 || (g_pRBRPlugin->m_iRBRTMCarSelectionType == 1 && g_pRBRPlugin->m_pRBRTMPlugin->selectedItemIdx == 1))) + { + int selectedCarIdx = ::RBRAPI_MapCarIDToMenuIdx(g_pRBRPlugin->m_pRBRTMPlugin->pRBRTMStageOptions1->selectedCarID); + + if (g_pRBRPlugin->m_carRBRTMPreviewTexture[selectedCarIdx].pTexture == nullptr && g_pRBRPlugin->m_carRBRTMPreviewTexture[selectedCarIdx].imgSize.cx >= 0 && g_RBRCarSelectionMenuEntry[selectedCarIdx].wszCarModel[0] != '\0') + { + g_pRBRPlugin->ReadCarPreviewImageFromFile(selectedCarIdx, + (float)g_pRBRPlugin->m_carRBRTMPictureRect.left, (float)g_pRBRPlugin->m_carRBRTMPictureRect.top, + (float)(g_pRBRPlugin->m_carRBRTMPictureRect.right - g_pRBRPlugin->m_carRBRTMPictureRect.left), + (float)(g_pRBRPlugin->m_carRBRTMPictureRect.bottom - g_pRBRPlugin->m_carRBRTMPictureRect.top), + &g_pRBRPlugin->m_carRBRTMPreviewTexture[selectedCarIdx]); + } + + // If the car preview image is successfully initialized (imgSize.cx >= 0) and texture (=image) is prepared then draw it on the screen + if (g_pRBRPlugin->m_carRBRTMPreviewTexture[selectedCarIdx].imgSize.cx >= 0 && g_pRBRPlugin->m_carRBRTMPreviewTexture[selectedCarIdx].pTexture != nullptr) + { + int iCarSpecPrintRow = 0; + int iFontHeight = g_pFontCarSpecCustom->GetTextHeight(); + PRBRCarSelectionMenuEntry pCarSelectionMenuEntry = &g_RBRCarSelectionMenuEntry[selectedCarIdx]; + + D3D9DrawVertexTex2D(g_pRBRIDirect3DDevice9, g_pRBRPlugin->m_carRBRTMPreviewTexture[selectedCarIdx].pTexture, g_pRBRPlugin->m_carRBRTMPreviewTexture[selectedCarIdx].vertexes2D); - // TODO: Better re-scaler function to map game resolution to screen resolution - RBRAPI_MapRBRPointToScreenPoint(rbrPosX, g_pRBRMenuSystem->menuImagePosY, &posX, &posY); - posY -= 5 * iFontHeight; + // 3D model and custom livery text is drawn on top of the car preview image (bottom left corner) + if (pCarSelectionMenuEntry->wszCarPhysicsLivery[0] != L'\0') + g_pFontCarSpecCustom->DrawText(g_pRBRPlugin->m_carRBRTMPictureRect.left + 2, g_pRBRPlugin->m_carRBRTMPictureRect.bottom - ((++iCarSpecPrintRow) * iFontHeight) - 4, C_CARSPECTEXT_COLOR, pCarSelectionMenuEntry->wszCarPhysicsLivery, 0); - if (g_pRBRPlugin->m_car3DModelInfoPosition.x != 0) - posX = g_pRBRPlugin->m_car3DModelInfoPosition.x; // Custom X-position - else if (rbrPosX == -1) - posX = ((g_rectRBRWndClient.right - g_rectRBRWndClient.left) / 2) - 50; // Default brute-force "center of the horizontal screen line" + if (pCarSelectionMenuEntry->wszCarPhysics3DModel[0] != L'\0') + g_pFontCarSpecCustom->DrawText(g_pRBRPlugin->m_carRBRTMPictureRect.left + 2, g_pRBRPlugin->m_carRBRTMPictureRect.bottom - ((++iCarSpecPrintRow) * iFontHeight) - 4, C_CARSPECTEXT_COLOR, pCarSelectionMenuEntry->wszCarPhysics3DModel, 0); - if (g_pRBRPlugin->m_car3DModelInfoPosition.y != 0) - posY = g_pRBRPlugin->m_car3DModelInfoPosition.y; // Custom Y-position + // FIACategory, HP, Year, Weight, Transmission + iCarSpecPrintRow = 0; + if (pCarSelectionMenuEntry->wszCarYear[0] != L'\0') + g_pFontCarSpecCustom->DrawText(g_pRBRPlugin->m_carRBRTMPictureRect.right + 12, g_pRBRPlugin->m_carRBRTMPictureRect.bottom - ((++iCarSpecPrintRow) * iFontHeight) - 4, C_CARSPECTEXT_COLOR, pCarSelectionMenuEntry->wszCarYear, 0); - PRBRCarSelectionMenuEntry pCarSelectionMenuEntry = &g_RBRCarSelectionMenuEntry[selectedCarIdx]; + if (pCarSelectionMenuEntry->wszCarTrans[0] != L'\0') + g_pFontCarSpecCustom->DrawText(g_pRBRPlugin->m_carRBRTMPictureRect.right + 12, g_pRBRPlugin->m_carRBRTMPictureRect.bottom - ((++iCarSpecPrintRow) * iFontHeight) - 4, C_CARSPECTEXT_COLOR, pCarSelectionMenuEntry->wszCarTrans, 0); - // Printout custom carSpec information (if the text line/value is not null string) - int iCarSpecPrintRow = 0; - if(pCarSelectionMenuEntry->wszCarPhysicsRevision[0] != L'\0') - g_pFontCarSpecCustom->DrawText(posX, (iCarSpecPrintRow++) * iFontHeight + posY, C_CARSPECTEXT_COLOR, pCarSelectionMenuEntry->wszCarPhysicsRevision, 0); + if (pCarSelectionMenuEntry->wszCarWeight[0] != L'\0') + g_pFontCarSpecCustom->DrawText(g_pRBRPlugin->m_carRBRTMPictureRect.right + 12, g_pRBRPlugin->m_carRBRTMPictureRect.bottom - ((++iCarSpecPrintRow) * iFontHeight) - 4, C_CARSPECTEXT_COLOR, pCarSelectionMenuEntry->wszCarWeight, 0); - if (pCarSelectionMenuEntry->wszCarPhysicsSpecYear[0] != L'\0') - g_pFontCarSpecCustom->DrawText(posX, (iCarSpecPrintRow++) * iFontHeight + posY, C_CARSPECTEXT_COLOR, pCarSelectionMenuEntry->wszCarPhysicsSpecYear, 0); + if (pCarSelectionMenuEntry->wszCarPower[0] != L'\0') + g_pFontCarSpecCustom->DrawText(g_pRBRPlugin->m_carRBRTMPictureRect.right + 12, g_pRBRPlugin->m_carRBRTMPictureRect.bottom - ((++iCarSpecPrintRow) * iFontHeight) - 4, C_CARSPECTEXT_COLOR, pCarSelectionMenuEntry->wszCarPower, 0); - if (pCarSelectionMenuEntry->wszCarPhysics3DModel[0] != L'\0') - g_pFontCarSpecCustom->DrawText(posX, (iCarSpecPrintRow++) * iFontHeight + posY, C_CARSPECTEXT_COLOR, pCarSelectionMenuEntry->wszCarPhysics3DModel, 0); + if (pCarSelectionMenuEntry->szCarCategory[0] != L'\0') + g_pFontCarSpecCustom->DrawText(g_pRBRPlugin->m_carRBRTMPictureRect.right + 12, g_pRBRPlugin->m_carRBRTMPictureRect.bottom - ((++iCarSpecPrintRow) * iFontHeight) - 4, C_CARSPECTEXT_COLOR, pCarSelectionMenuEntry->szCarCategory, 0); - if (pCarSelectionMenuEntry->wszCarPhysicsLivery[0] != L'\0') - g_pFontCarSpecCustom->DrawText(posX, (iCarSpecPrintRow++) * iFontHeight + posY, C_CARSPECTEXT_COLOR, pCarSelectionMenuEntry->wszCarPhysicsLivery, 0); + if (pCarSelectionMenuEntry->wszCarModel[0] != L'\0') + g_pFontCarSpecCustom->DrawText(g_pRBRPlugin->m_carRBRTMPictureRect.right + 12, g_pRBRPlugin->m_carRBRTMPictureRect.bottom - ((++iCarSpecPrintRow) * iFontHeight) - 4, C_CARMODELTITLETEXT_COLOR, pCarSelectionMenuEntry->wszCarModel, 0); - if (pCarSelectionMenuEntry->wszCarPhysicsCustomTxt[0] != L'\0') - g_pFontCarSpecCustom->DrawText(posX, (iCarSpecPrintRow++) * iFontHeight + posY, C_CARSPECTEXT_COLOR, pCarSelectionMenuEntry->wszCarPhysicsCustomTxt, 0); + } + } + } + else if (g_pRBRPlugin->m_iRBRTMPluginMenuIdx == 0) + { + // RBRTM integration is enabled, but the RBRTM linking is not yet initialized. Do it now when RBR has already loaded all custom plugins. This is done only once per RBR process runtime. + // If initialization succeeds then m_iRBRTMPluginMenuIdx > 0 and m_pRBRTMPlugig != NULL, otherwise -1 and NULL. + g_pRBRPlugin->InitRBRTMPluginIntegration(); + } } } else if (g_pRBRPlugin->m_bCustomReplayShowCroppingRect && g_pRBRPlugin->m_iCustomReplayState >= 2 && g_pRBRPlugin->m_iCustomReplayState != 4) { - // Draw rectangle to highlight the screenshot capture area + // Draw rectangle to highlight the screenshot capture area (except when state == 4 because then this plugin takes the car preview screenshot) D3D9DrawVertex2D(g_pRBRIDirect3DDevice9, g_pRBRPlugin->m_screenshotCroppingRectVertexBuffer); } #if USE_DEBUG == 1 WCHAR szTxtBuffer[200]; - swprintf_s(szTxtBuffer, COUNT_OF_ITEMS(szTxtBuffer), L"Mode %d %d. Img (%f,%f)(%f,%f) Timer=%f", g_pRBRGameMode->gameMode, g_pRBRGameModeExt->gameModeExt, g_pRBRMenuSystem->menuImagePosX, g_pRBRMenuSystem->menuImagePosY, g_pRBRMenuSystem->menuImageWidth, g_pRBRMenuSystem->menuImageHeight, g_pRBRCarInfo->stageStartCountdown); - g_pFontDebug->DrawText(1, 1 * 20, C_DEBUGTEXT_COLOR, szTxtBuffer, 0); + //swprintf_s(szTxtBuffer, COUNT_OF_ITEMS(szTxtBuffer), L"Mode %d %d. Img (%f,%f)(%f,%f) Timer=%f", g_pRBRGameMode->gameMode, g_pRBRGameModeExt->gameModeExt, g_pRBRMenuSystem->menuImagePosX, g_pRBRMenuSystem->menuImagePosY, g_pRBRMenuSystem->menuImageWidth, g_pRBRMenuSystem->menuImageHeight, g_pRBRCarInfo->stageStartCountdown); + //g_pFontDebug->DrawText(1, 1 * 20, C_DEBUGTEXT_COLOR, szTxtBuffer, 0); +/* RECT wndRect; RECT wndClientRect; RECT wndMappedRect; @@ -1808,10 +2027,12 @@ HRESULT __fastcall CustomRBRDirectXEndScene(void* objPointer) CopyRect(&wndMappedRect, &wndClientRect); MapWindowPoints(creationParameters.hFocusWindow, NULL, (LPPOINT)&wndMappedRect, 2); + swprintf_s(szTxtBuffer, COUNT_OF_ITEMS(szTxtBuffer), L"hWnd=%x Win=(%d,%d)(%d,%d) Client=(%d,%d)(%d,%d)", (int)creationParameters.hFocusWindow, wndRect.left, wndRect.top, wndRect.right, wndRect.bottom, wndClientRect.left, wndClientRect.top, wndClientRect.right, wndClientRect.bottom); g_pFontDebug->DrawText(1, 2 * 20, C_DEBUGTEXT_COLOR, szTxtBuffer, 0); +*/ //swprintf_s(szTxtBuffer, COUNT_OF_ITEMS(szTxtBuffer), L"Mapped=(%d,%d)(%d,%d) GameRes=(%d,%d)", wndMappedRect.left, wndMappedRect.top, wndMappedRect.right, wndMappedRect.bottom, g_pRBRGameConfig->resolutionX, g_pRBRGameConfig->resolutionY); //g_pFontDebug->DrawText(1, 3 * 20, C_DEBUGTEXT_COLOR, szTxtBuffer, 0); @@ -1849,7 +2070,7 @@ HRESULT __fastcall CustomRBRDirectXEndScene(void* objPointer) // Take a RBR car preview screenshot and save it as PNG preview file. // At this point the cropping highlight rectangle is hidden, so it is not shown in the screenshot. std::string imgExtension = "."; - imgExtension = imgExtension + g_RBRPluginMenu_ImageOptions[g_pRBRPlugin->m_iMenuImageOption]; // .PNG or .BMP img file format + imgExtension = imgExtension + g_NGPCarMenu_ImageOptions[g_pRBRPlugin->m_iMenuImageOption]; // .PNG or .BMP img file format _ToLowerCase(imgExtension); std::wstring outputFileName = g_pRBRPlugin->m_screenshotPath + L"\\" + g_RBRCarSelectionMenuEntry[RBRAPI_MapCarIDToMenuIdx(g_pRBRPlugin->m_iCustomReplayCarID)].wszCarModel + _ToWString(imgExtension); diff --git a/src/NGPCarMenu.h b/src/NGPCarMenu.h index 02fd0c2..20d6daf 100644 --- a/src/NGPCarMenu.h +++ b/src/NGPCarMenu.h @@ -48,11 +48,14 @@ #define C_DEBUGTEXT_COLOR D3DCOLOR_ARGB(255, 255,255,255) // White -#define C_CARSPECTEXT_COLOR D3DCOLOR_ARGB(255, 0xE0,0xE0,0xE0) // Grey-White + +#define C_CARSPECTEXT_COLOR D3DCOLOR_ARGB(255, 0xE0,0xE0,0xE0) // Grey-White +#define C_CARMODELTITLETEXT_COLOR D3DCOLOR_ARGB(255, 0x7F,0x7F,0xFE) // Light-blue #define C_REPLAYFILENAME_SCREENSHOT "_NGPCarMenu.rpl" // Name of the temporary RBR replay file (char and wchar version) #define C_REPLAYFILENAME_SCREENSHOTW L"_NGPCarMenu.rpl" +#define C_RBRTM_PLUGIN_NAME "RBR Tournament" // @@ -93,6 +96,31 @@ typedef struct { } RBRCarSelectionMenuEntry; typedef RBRCarSelectionMenuEntry* PRBRCarSelectionMenuEntry; +//------------------------------------------------------------------------------------------------ +typedef struct { +#pragma pack(push,1) + BYTE unknown1; + BYTE unknown2; + BYTE serviceMins; // 0 = Shakedown without service parks. >1 = Mins of the next service park + BYTE selectedCarID; // 00..07 = Selected car slot# +#pragma pack(pop) +} RBRTMStageOptions1; +typedef RBRTMStageOptions1* PRBRTMStageOptions1; + +typedef struct { +#pragma pack(push,1) + __int32 unknown1; // 0x00 + __int32 unknown2; // 0x04 + __int32 unknown3; // 0x08 + DWORD* pCurrentRBRTMMenuObj; // 0x0C - Pointer to current RBRTM menu object + __int32 selectedItemIdx; // 0x10 - Currently selected menu item line + __int32 selectedStage; // 0x14 - Currently selected shakedown stage# (map) + + BYTE pad1[0xBA2 - 0x14 - sizeof(__int32)]; + PRBRTMStageOptions1 pRBRTMStageOptions1; // 0xBA2 +#pragma pack(pop) +} RBRTMPlugin; +typedef RBRTMPlugin* PRBRTMPlugin; //------------------------------------------------------------------------------------------------ @@ -142,6 +170,7 @@ class CNGPCarMenu : public IPlugin int m_iMenuSelection; // Currently selected plugin menu item idx int m_iMenuCreateOption; // 0 = Generate all car images, 1 = Generate only missing car images int m_iMenuImageOption; // 0 = Use PNG preview file format to read and create image files, 1 = BMP file format + int m_iMenuRBRTMOption; // 0 = RBRTM integration disabled, 1 = Enabled std::string m_sRBRRootDir; // RBR app path, multibyte (or normal ASCII) string std::wstring m_sRBRRootDirW; // RBR app path, widechar string @@ -153,14 +182,18 @@ class CNGPCarMenu : public IPlugin std::wstring m_rbrCITCarListFilePath; // Path to RBRCIT carList.ini file (the file has NGP car details and specs information) std::wstring m_easyRBRFilePath; // Path to EesyRBR installation folder (if RBRCIT car manager is not used) - RECT m_screenshotCroppingRect; // Cropping rect of a screenshot (in RBR window coordinates) - RECT m_carSelectLeftBlackBarRect; // Black bar on the left and right side of the "Select Car" menu (used to hide the default background image) - RECT m_carSelectRightBlackBarRect; + RECT m_screenshotCroppingRect; // Cropping rect of a screenshot (in RBR window coordinates) + RECT m_carSelectLeftBlackBarRect; // Black bar on the left and right side of the "Select Car" menu (used to hide the default background image) + RECT m_carSelectRightBlackBarRect; // (see above) POINT m_car3DModelInfoPosition; // X Y position of the car 3D info textbox. If Y is 0 then the plugin uses the default Y location (few lines above the car preview image). + RECT m_carRBRTMPictureRect; // Output rect of RBRTM car preview image (re-scaled pic area) + RECT m_carRBRTMPictureCropping; // Optional cropping area of the normal car preview image to be used as RBRTM preview image (0 0 0 0 = Re-scales the whole picture to fit the RBRTM pic rect) + //POINT m_carRBRTM3DModelInfoPosition; // X Y position of the car info textbox (FIA Category, HP, Transmission, Weight, Year) + LPDIRECT3DVERTEXBUFFER9 m_screenshotCroppingRectVertexBuffer; // Screeshot rect vertex to highlight the current capture area on screen while capturing preview img - int m_iCustomReplayState; // 0 = No custom replay (default RBR behaviour), 1 = Custom replay process is running. Take screenshots of a car models. + int m_iCustomReplayState; // 0 = No custom replay (default RBR behaviour), 1 = Custom replay process is running. This plugin takes screenshots of car models std::chrono::steady_clock::time_point m_tCustomReplayStateStartTime; int m_iCustomReplayCarID; // 0..7 = The current carID used in a custom screenshot replay file @@ -168,7 +201,12 @@ class CNGPCarMenu : public IPlugin bool m_bCustomReplayShowCroppingRect; // Show the current car preview screenshot cropping rect area on screen (ie. few secs before the screenshot is taken) IMAGE_TEXTURE m_carPreviewTexture[8]; // 0..7 car preview image data (or NULL if missing/not loaded yet) + IMAGE_TEXTURE m_carRBRTMPreviewTexture[8]; // 0..7 car preview image for RBRTM plugin integration if RBRTM integration is enabled (the same pic as in standard preview image texture, but re-scaled to fit the smaller picture are in RBRTM screen) + int m_iRBRTMPluginMenuIdx; // Index of the RBRTM plugin in the RBR Plugins menu list (this way we know when RBRTM custom plugin in Nth index position is activated) + bool m_bRBRTMPluginActive; // TRUE/FALSE if the current active custom plugin is RBRTM (active = The RBRTM plugin handler is running in foreground) + int m_iRBRTMCarSelectionType; // 0=No car selection menu shown, 1=Online Tournament selection, 2=Shakedown car selection + PRBRTMPlugin m_pRBRTMPlugin; // Pointer to RBRTM plugin or nullptr if not found or RBRTM integration is disabled //------------------------------------------------------------------------------------------------ @@ -178,6 +216,10 @@ class CNGPCarMenu : public IPlugin int GetNextScreenshotCarID(int currentCarID); static bool PrepareScreenshotReplayFile(int carID); + bool ReadCarPreviewImageFromFile(int selectedCarIdx, float x, float y, float cx, float cy, IMAGE_TEXTURE* pOutImageTexture); + + bool InitRBRTMPluginIntegration(); + void RefreshSettingsFromPluginINIFile(); void SaveSettingsToPluginINIFile(); diff --git a/src/NGPCarMenu.ini b/src/NGPCarMenu.ini index 16de866..8b2cb1c 100644 --- a/src/NGPCarMenu.ini +++ b/src/NGPCarMenu.ini @@ -40,6 +40,10 @@ ; Car3DModelInfoPosition= X Y position of the the car 3D model info textbox. If this is empty or missing then the game uses default location. Example "940 600" (without quotes). ; If there is a single value then it is assumed to be just X position and Y position will be at default location. Example "940" (without quotes). ; +; RBRTM_Integration = 0 or 1. 0=RBRTM plugin integration disabled. 1=Integration enabled if the RBRTM plugin is installed and it is V0.88 version. +; RBRTM_CarPictureRect = Rectable area where the car preview image is drawn in Shakedown and OnlineTournament "car selection" menu (bottom left "empty" area in RBRTM Shakedown menu. Value should be in left top right botttom, 0 0 0 0 format). Tweak this value if the picture is drawn at wrong location. +; RBRTM_CarPictureCropping = Cropping rectangle within the original preview image. The cropping result is then drawn in RBRTM menus (not yet implemented). +; ; The plugin supports resolution specific preview images. Feel free to add new resolution specific configuration entries if the one you use is missing. ; @@ -61,6 +65,12 @@ RBRCITCarListPath=RBRCIT\carlist\carList.ini ; ;EasyRBRPath=c:\apps\EasyRBR\easyrbr.ini +; +; Integration with RBRTM Czech tournaments plugin (ie. show car preview and details in Shakedown and OnlineTournament car selection menus). +; 0=Disabled, 1=Enabled +; +RBRTM_Integration = 1 + ; ; Following entires are horizontal x vertical RBR game resolutions. If your resolution is not there then you should add it and tweak ScreenCropping rectangle location. @@ -74,95 +84,127 @@ ScreenshotCropping=0 236 1920 780 CarSelectLeftBlackBar=0 0 238 1080 CarSelectRightBlackBar=1682 0 1920 1080 Car3DModelInfoPosition= +RBRTM_CarPictureRect = 238 548 1260 1040 +RBRTM_CarPictureCropping = [1680x1050] ScreenshotCropping=0 250 1680 750 CarSelectLeftBlackBar=0 0 138 1050 CarSelectRightBlackBar=1542 0 1680 1050 Car3DModelInfoPosition= +RBRTM_CarPictureRect = 138 534 1130 1010 +RBRTM_CarPictureCropping = [5120x2880] ScreenshotCropping=0 236 5120 780 CarSelectLeftBlackBar= CarSelectRightBlackBar= Car3DModelInfoPosition= +RBRTM_CarPictureRect = +RBRTM_CarPictureCropping = [3840x2160] ScreenshotCropping=0 236 3840 780 CarSelectLeftBlackBar= CarSelectRightBlackBar= Car3DModelInfoPosition= +RBRTM_CarPictureRect = +RBRTM_CarPictureCropping = [3840x1600] ScreenshotCropping=0 236 3840 780 CarSelectLeftBlackBar= CarSelectRightBlackBar= Car3DModelInfoPosition= +RBRTM_CarPictureRect = +RBRTM_CarPictureCropping = [3440x1440] ScreenshotCropping=0 236 3440 780 CarSelectLeftBlackBar= CarSelectRightBlackBar= Car3DModelInfoPosition= +RBRTM_CarPictureRect = +RBRTM_CarPictureCropping = [2560x1080] ScreenshotCropping=0 236 2560 780 CarSelectLeftBlackBar= CarSelectRightBlackBar= Car3DModelInfoPosition= +RBRTM_CarPictureRect = +RBRTM_CarPictureCropping = [2560x1440] ScreenshotCropping=0 236 2560 780 CarSelectLeftBlackBar= CarSelectRightBlackBar= Car3DModelInfoPosition= +RBRTM_CarPictureRect = +RBRTM_CarPictureCropping = [1600x900] -ScreenshotCropping=0 220 1600 750 +ScreenshotCropping = 0 210 1600 660 CarSelectLeftBlackBar= CarSelectRightBlackBar= Car3DModelInfoPosition= +RBRTM_CarPictureRect = 199 460 1047 866 +RBRTM_CarPictureCropping = [1366x768] ScreenshotCropping=0 176 1366 550 CarSelectLeftBlackBar=0 0 169 768 CarSelectRightBlackBar=1198 0 1366 768 Car3DModelInfoPosition= +RBRTM_CarPictureRect = 169 394 895 739 +RBRTM_CarPictureCropping = [1360x768] ScreenshotCropping=0 176 1360 550 CarSelectLeftBlackBar=0 0 166 768 CarSelectRightBlackBar=1195 0 1360 768 Car3DModelInfoPosition= +RBRTM_CarPictureRect = 166 394 892 739 +RBRTM_CarPictureCropping = [1280x1024] -ScreenshotCropping=0 320 1280 570 +ScreenshotCropping = 0 250 1280 710 CarSelectLeftBlackBar= CarSelectRightBlackBar= Car3DModelInfoPosition= +RBRTM_CarPictureRect = +RBRTM_CarPictureCropping = [1280x800] -ScreenshotCropping=0 320 1280 570 +ScreenshotCropping = 0 190 1280 570 CarSelectLeftBlackBar= CarSelectRightBlackBar= Car3DModelInfoPosition= +RBRTM_CarPictureRect = 106 410 860 769 +RBRTM_CarPictureCropping = [1280x720] -ScreenshotCropping=0 320 1280 570 +ScreenshotCropping = 0 168 1280 530 CarSelectLeftBlackBar= CarSelectRightBlackBar= Car3DModelInfoPosition= +RBRTM_CarPictureRect = 159 370 838 694 +RBRTM_CarPictureCropping = [1024x768] ScreenshotCropping=0 180 1024 550 CarSelectLeftBlackBar= CarSelectRightBlackBar= Car3DModelInfoPosition= +RBRTM_CarPictureRect = +RBRTM_CarPictureCropping = [640x480] -ScreenshotCropping=0 300 640 400 +ScreenshotCropping = 0 110 640 360 CarSelectLeftBlackBar= CarSelectRightBlackBar= Car3DModelInfoPosition= +RBRTM_CarPictureRect = +RBRTM_CarPictureCropping = ; PS. This file needs to be in UTF8 format, so if you have accidentally saved this in legacy ASCII format then re-save the file in UTF8 diff --git a/src/NGPCarMenu.rc b/src/NGPCarMenu.rc index 4e80953..649e83e 100644 --- a/src/NGPCarMenu.rc +++ b/src/NGPCarMenu.rc @@ -51,8 +51,8 @@ END // VS_VERSION_INFO VERSIONINFO - FILEVERSION 1,0,6,0 - PRODUCTVERSION 1,0,6,0 + FILEVERSION 1,0,7,0 + PRODUCTVERSION 1,0,7,0 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L @@ -69,12 +69,12 @@ BEGIN BEGIN VALUE "CompanyName", "MIKA-N" VALUE "FileDescription", "NGPCarMenu - Customized ""Select Car"" game menu for Richard Burns Rally (RBR) 1.02 SSE" - VALUE "FileVersion", "1.0.6.0" + VALUE "FileVersion", "1.0.7.0" VALUE "InternalName", "NGPCarMe.dll" VALUE "LegalCopyright", "Copyright (C) 2020 by mika-n. All rights reserved. See LicenseText.txt for more information. This tool is distributed free of charge. Use at your own risk." VALUE "OriginalFilename", "NGPCarMenu.dll" VALUE "ProductName", "NGPCarMenu" - VALUE "ProductVersion", "1.0.6.0" + VALUE "ProductVersion", "1.0.7.0" END END BLOCK "VarFileInfo" diff --git a/src/RBRAPI.h b/src/RBRAPI.h index 3cccc66..af6c783 100644 --- a/src/RBRAPI.h +++ b/src/RBRAPI.h @@ -89,6 +89,8 @@ extern BOOL WriteOpCodeHexString(const LPVOID writeAddr, LPCSTR sHexText); extern BOOL WriteOpCodeBuffer(const LPVOID writeAddr, const BYTE* buffer, const int iBufLen); extern BOOL WriteOpCodePtr(const LPVOID writeAddr, const LPVOID ptrValue); +extern BOOL ReadOpCodePtr(const LPVOID readAddr, LPVOID* ptrValue); + extern int RBRAPI_MapCarIDToMenuIdx(int carID); // 00..07 carID is not the same as order of car selection items extern int RBRAPI_MenuIdxToCarID(int menuIdx); @@ -367,7 +369,7 @@ typedef struct { typedef RBRMenuItemCarSelectionCarSpecTexts* PRBRMenuItemCarSelectionCarSpecTexts; -// Menu object (RBRMenuPoint has refrences to these objects) +// Menu object (RBRMenuPoint has references to these objects) struct RBRMenuObj; typedef struct RBRMenuObj* PRBRMenuObj; typedef struct RBRMenuObj RBRMenuObj; @@ -500,7 +502,28 @@ typedef struct { } RBRMenuSystem; typedef RBRMenuSystem* PRBRMenuSystem; -// TODO. Offset 0x165FC48 Some other menu objects? + +// Offset 0x165FC48 +typedef struct { +#pragma pack(push,1) + BYTE pad1[0x03E8]; // 0x00 + PRBRMenuObj optionsMenuObj; // 0x3E8 (RBR Options menu) + BYTE pad2[0x43C - 0x3E8 - sizeof(PRBRMenuObj)]; + LPVOID customPluginMenuObj; // 0x43C (RBRMenuSystem.currentMenuObj points to this value when a custom plugin menu is open and not a standard RBR in-game menu) + PRBRMenuObj pluginsMenuObj; // 0x440 (RBR Plugins menuObj. Lists all custom plugin names +#pragma pack(pop) +} RBRPluginMenuSystem; +typedef RBRPluginMenuSystem* PRBRPluginMenuSystem; + +// pluginsMenuObj->pItemObj pointers +typedef struct { +#pragma pack(push,1) + BYTE pad1[0x20]; // 0x00 + LPCSTR szPluginName; // 0x20 +#pragma pack(pop) +} RBRPluginMenuSystemItemObj; +typedef RBRPluginMenuSystemItemObj* PRBRPluginMenuSystemItemObj; + // Global RBR object pointers extern PRBRGameConfig g_pRBRGameConfig;