diff --git a/src/CreateReleaseZip.cmd b/src/CreateReleaseZip.cmd new file mode 100644 index 0000000..19b2cee --- /dev/null +++ b/src/CreateReleaseZip.cmd @@ -0,0 +1,60 @@ +@echo off + +SET APPNAME=NGPCarMenu +SET VERSIONTAG=%~1 +SET RELEASE_FOLDER=Release\VER_%VERSIONTAG% +SET RELEASE_PKG=%APPNAME%_%VERSIONTAG%.zip + +SET ZIP_TOOL=C:\Program Files\7-Zip\7z.exe + +echo [%DATE% %TIME%] %~nx0 %APPNAME% %VERSIONTAG% +echo [%DATE% %TIME%] Release folder %RELEASE_FOLDER% +echo [%DATE% %TIME%] Release package %RELEASE_PKG% + + +if "%~1" == "" ( + echo Missing cmdline argument: versionTag + echo Example: CreateReleaseZip.cmd 1.14 + goto END +) + +if NOT EXIST "Release" ( + echo Release folder missing. Re-build the project in C++ compiler usign Release configuration + goto END +) + +if NOT EXIST "Release\%APPNAME%.dll" ( + echo %APPNAME%.dll Re-build the project in C++ compiler using Release configuration + goto END +) + +echo Do you want to create a release package %APPNAME%_%VERSIONTAG%.zip? +echo Press CTRL-C to quit, other key to continue... +pause + + +mkdir "%RELEASE_FOLDER%\" +mkdir "%RELEASE_FOLDER%\Replays\" +mkdir "%RELEASE_FOLDER%\Plugins\" +mkdir "%RELEASE_FOLDER%\Plugins\%APPNAME%\" +mkdir "%RELEASE_FOLDER%\Plugins\%APPNAME%\preview\1920x1080\" +mkdir "%RELEASE_FOLDER%\Plugins\%APPNAME%\preview\1366x768\" + +copy "Release\%APPNAME%.dll" "%RELEASE_FOLDER%\Plugins\" +copy "%APPNAME%.ini" "%RELEASE_FOLDER%\Plugins\" +copy "%APPNAME%.rpl" "%RELEASE_FOLDER%\Replays\" +copy "..\LicenseText.txt" "%RELEASE_FOLDER%\Plugins\%APPNAME%\" +copy "..\ReadMe.md" "%RELEASE_FOLDER%\Plugins\%APPNAME%\" +copy "..\ReadMe.md" "%RELEASE_FOLDER%\Plugins\%APPNAME%\ReadMe.txt" + +type NUL > "%RELEASE_FOLDER%\Plugins\%APPNAME%\preview\1920x1080\carImages.txt" +type NUL > "%RELEASE_FOLDER%\Plugins\%APPNAME%\preview\1366x768\carImages.txt" + +PUSHD "%RELEASE_FOLDER%\" +del "..\%RELEASE_PKG%" +"%ZIP_TOOL%" a -r -tzip "..\%RELEASE_PKG%" *.* +POPD + +echo [%DATE% %TIME%] Release\VER_%VERSIONTAG% package build completed + +:END diff --git a/src/D3D9Helpers.cpp b/src/D3D9Helpers.cpp index 5087c1a..7091d80 100644 --- a/src/D3D9Helpers.cpp +++ b/src/D3D9Helpers.cpp @@ -34,6 +34,10 @@ #include "D3D9Helpers.h" +#if USE_DEBUG == 1 +#include "NGPCarMenu.h" +#endif + namespace fs = std::filesystem; // @@ -167,6 +171,118 @@ bool _StringToRect(const std::wstring& s, RECT* outRect, const wchar_t separator return (items.size() >= 4); } + +//--------------------------------------------------------------------------------------------------------------- +#if USE_DEBUG == 1 + +//----------------------------------------------------------------------------------------------------------------------------- +// Debug printout functions. Not used in release build. +// + +FILE* g_fpDebugLogFile = nullptr; +void DebugPrintCloseFile(); + +void DebugPrintFunc(LPCSTR lpszFormat, ...) +{ + va_list args; + va_start(args, lpszFormat); + + SYSTEMTIME t; + char szTxtTimeStampBuf[32]; + char szTxtBuf[1024]; + + try + { + GetLocalTime(&t); + if (GetTimeFormat(LOCALE_USER_DEFAULT, 0, &t, "hh:mm:ss ", (LPSTR)szTxtTimeStampBuf, sizeof(szTxtTimeStampBuf) - 1) <= 0) + szTxtTimeStampBuf[0] = '\0'; + + if (_vsnprintf_s(szTxtBuf, sizeof(szTxtBuf) - 1, lpszFormat, args) <= 0) + szTxtBuf[0] = '\0'; + + if (g_fpDebugLogFile == nullptr) + fopen_s(&g_fpDebugLogFile, "NGPCarMenu.log", "a+t"); + + if (g_fpDebugLogFile) + { + fprintf(g_fpDebugLogFile, szTxtTimeStampBuf); + fprintf(g_fpDebugLogFile, szTxtBuf); + fprintf(g_fpDebugLogFile, "\n"); + } + } + catch (...) + { + // Do nothing + } + + va_end(args); +} + +void DebugPrintEmptyFile() +{ + FILE* fpDebugLogFile = nullptr; + DebugPrintCloseFile(); + fopen_s(&fpDebugLogFile, "NGPCarMenu.log", "w"); + if (fpDebugLogFile != nullptr) fclose(fpDebugLogFile); +} + +void DebugPrintCloseFile() +{ + FILE* fpDebugLogFile = g_fpDebugLogFile; + g_fpDebugLogFile = nullptr; + if (fpDebugLogFile != nullptr) fclose(fpDebugLogFile); +} + +void DebugDumpBuffer(byte* pBuffer, int iPreOffset = 0, int iBytesToDump = 64) +{ + char txtBuffer[64]; + + byte* pBuffer2 = pBuffer - iPreOffset; + for (int idx = 0; idx < iBytesToDump; idx += 8) + { + int iAppendPos = 0; + for (int idx2 = 0; idx2 < 8; idx2++) + iAppendPos += sprintf_s(txtBuffer + iAppendPos, sizeof(txtBuffer), "%02x ", (byte)pBuffer2[idx + idx2]); + + iAppendPos += sprintf_s(txtBuffer + iAppendPos, sizeof(txtBuffer), " | "); + + for (int idx2 = 0; idx2 < 8; idx2++) + iAppendPos += sprintf_s(txtBuffer + iAppendPos, sizeof(txtBuffer), "%c", (pBuffer2[idx + idx2] < 32 || pBuffer2[idx + idx2] > 126) ? '.' : pBuffer2[idx + idx2]); + + DebugPrint(txtBuffer); + } +} + +void DebugDumpBufferToScreen(byte* pBuffer, int iPreOffset = 0, int iBytesToDump = 64, int posX = 850, int posY = 1) +{ + WCHAR txtBuffer[64]; + + D3DRECT rec = { posX, posY, posX + 440, posY + ((iBytesToDump / 8) * 20) }; + g_pRBRIDirect3DDevice9->Clear(1, &rec, D3DCLEAR_TARGET, D3DCOLOR_ARGB(255, 50, 50, 50), 0, 0); + + byte* pBuffer2 = pBuffer - iPreOffset; + for (int idx = 0; idx < iBytesToDump; idx += 8) + { + int iAppendPos = 0; + + iAppendPos += swprintf_s(txtBuffer + iAppendPos, COUNT_OF_ITEMS(txtBuffer) - iAppendPos, L"%04x ", idx); + for (int idx2 = 0; idx2 < 8; idx2++) + iAppendPos += swprintf_s(txtBuffer + iAppendPos, COUNT_OF_ITEMS(txtBuffer) - iAppendPos, L"%02x ", (byte)pBuffer2[idx + idx2]); + + iAppendPos += swprintf_s(txtBuffer + iAppendPos, COUNT_OF_ITEMS(txtBuffer) - iAppendPos, L" | "); + + for (int idx2 = 0; idx2 < 8; idx2++) + iAppendPos += swprintf_s(txtBuffer + iAppendPos, COUNT_OF_ITEMS(txtBuffer) - iAppendPos, L"%c", (pBuffer2[idx + idx2] < 32 || pBuffer2[idx + idx2] > 126) ? L'.' : pBuffer2[idx + idx2]); + + pFontDebug->DrawText(posX, (idx / 8) * 20 + posY, D3DCOLOR_ARGB(255, 240, 250, 0), txtBuffer, 0); + //pFontDebug->DrawText(posX, (idx / 8) * 20 + posY, D3DCOLOR_ARGB(255, 240, 250, 0), txtBuffer, 0); + } +} + +#endif // USE_DEBUG + + + //--------------------------------------------------------------------------------------------------------------- // diff --git a/src/D3D9Helpers.h b/src/D3D9Helpers.h index f592004..0490e72 100644 --- a/src/D3D9Helpers.h +++ b/src/D3D9Helpers.h @@ -25,6 +25,36 @@ //#include //#include + +//------------------------------------------------------------------------------------------------ + +#ifdef _DEBUG +#define USE_DEBUG 1 // 1=Custom debug outputs and calculations (spends few cycles of precious CPU time), 0=No custom debugging, run the game code without extra debug code +#endif + +#ifndef _DEBUG +#undef USE_DEBUG +#endif + + +#if USE_DEBUG == 1 +#include "D3D9Font\D3DFont.h" + +#define DebugPrint DebugPrintFunc // DEBUG version to dump logfile messages +#else +#define DebugPrint // RELEASE version of DebugPrint doing nothing +#endif + +#if USE_DEBUG == 1 +extern CD3DFont* pFontDebug; + +extern void DebugPrintCloseFile(); +extern void DebugPrintEmptyFile(); +extern void DebugPrintFunc(LPCSTR lpszFormat, ...); +#endif + +//------------------------------------------------------------------------------------------------ + #ifndef SAFE_RELEASE #define SAFE_RELEASE( p ) if( p ){ p->Release(); p = nullptr; } #endif diff --git a/src/NGPCarMenu.cpp b/src/NGPCarMenu.cpp index f1646aa..c12ba17 100644 --- a/src/NGPCarMenu.cpp +++ b/src/NGPCarMenu.cpp @@ -116,115 +116,6 @@ char* g_RBRPluginMenu_CreateOptions[2] = { }; -#if USE_DEBUG == 1 - -//----------------------------------------------------------------------------------------------------------------------------- -// Debug printout functions. Not used in release build. -// - -FILE* g_fpDebugLogFile = nullptr; -void DebugPrintCloseFile(); - -void DebugPrintFunc(LPCSTR lpszFormat, ...) -{ - va_list args; - va_start(args, lpszFormat); - - SYSTEMTIME t; - char szTxtTimeStampBuf[32]; - char szTxtBuf[1024]; - - try - { - GetLocalTime(&t); - if (GetTimeFormat(LOCALE_USER_DEFAULT, 0, &t, "hh:mm:ss ", (LPSTR)szTxtTimeStampBuf, sizeof(szTxtTimeStampBuf) - 1) <= 0) - szTxtTimeStampBuf[0] = '\0'; - - if (_vsnprintf_s(szTxtBuf, sizeof(szTxtBuf) - 1, lpszFormat, args) <= 0) - szTxtBuf[0] = '\0'; - - if (g_fpDebugLogFile == nullptr) - fopen_s(&g_fpDebugLogFile, "NGPCarMenu.log", "a+t"); - - if (g_fpDebugLogFile) - { - fprintf(g_fpDebugLogFile, szTxtTimeStampBuf); - fprintf(g_fpDebugLogFile, szTxtBuf); - fprintf(g_fpDebugLogFile, "\n"); - } - } - catch (...) - { - // Do nothing - } - - va_end(args); -} - -void DebugPrintEmptyFile() -{ - FILE* fpDebugLogFile = nullptr; - DebugPrintCloseFile(); - fopen_s(&fpDebugLogFile, "NGPCarMenu.log", "w"); - if (fpDebugLogFile != nullptr) fclose(fpDebugLogFile); -} - -void DebugPrintCloseFile() -{ - FILE* fpDebugLogFile = g_fpDebugLogFile; - g_fpDebugLogFile = nullptr; - if (fpDebugLogFile != nullptr) fclose(fpDebugLogFile); -} - -void DebugDumpBuffer(byte* pBuffer, int iPreOffset = 0, int iBytesToDump = 64) -{ - char txtBuffer[64]; - - byte* pBuffer2 = pBuffer - iPreOffset; - for (int idx = 0; idx < iBytesToDump; idx += 8) - { - int iAppendPos = 0; - for (int idx2 = 0; idx2 < 8; idx2++) - iAppendPos += sprintf_s(txtBuffer + iAppendPos, sizeof(txtBuffer), "%02x ", (byte)pBuffer2[idx + idx2]); - - iAppendPos += sprintf_s(txtBuffer + iAppendPos, sizeof(txtBuffer), " | "); - - for (int idx2 = 0; idx2 < 8; idx2++) - iAppendPos += sprintf_s(txtBuffer + iAppendPos, sizeof(txtBuffer), "%c", (pBuffer2[idx + idx2] < 32 || pBuffer2[idx + idx2] > 126) ? '.' : pBuffer2[idx + idx2]); - - DebugPrint(txtBuffer); - } -} - -void DebugDumpBufferToScreen(byte* pBuffer, int iPreOffset = 0, int iBytesToDump = 64, int posX = 850, int posY = 1) -{ - WCHAR txtBuffer[64]; - - D3DRECT rec = { posX, posY, posX + 440, posY + ((iBytesToDump / 8) * 20) }; - g_pRBRIDirect3DDevice9->Clear(1, &rec, D3DCLEAR_TARGET, D3DCOLOR_ARGB(255, 50, 50, 50), 0, 0); - - byte* pBuffer2 = pBuffer - iPreOffset; - for (int idx = 0; idx < iBytesToDump; idx += 8) - { - int iAppendPos = 0; - - iAppendPos += swprintf_s(txtBuffer + iAppendPos, COUNT_OF_ITEMS(txtBuffer) - iAppendPos, L"%04x ", idx); - for (int idx2 = 0; idx2 < 8; idx2++) - iAppendPos += swprintf_s(txtBuffer + iAppendPos, COUNT_OF_ITEMS(txtBuffer) - iAppendPos, L"%02x ", (byte)pBuffer2[idx + idx2]); - - iAppendPos += swprintf_s(txtBuffer + iAppendPos, COUNT_OF_ITEMS(txtBuffer) - iAppendPos, L" | "); - - for (int idx2 = 0; idx2 < 8; idx2++) - iAppendPos += swprintf_s(txtBuffer + iAppendPos, COUNT_OF_ITEMS(txtBuffer) - iAppendPos, L"%c", (pBuffer2[idx + idx2] < 32 || pBuffer2[idx + idx2] > 126) ? L'.' : pBuffer2[idx + idx2]); - - pFontDebug->DrawText(posX, (idx / 8) * 20 + posY, D3DCOLOR_ARGB(255, 240, 250, 0), txtBuffer, 0); - //pFontDebug->DrawText(posX, (idx / 8) * 20 + posY, D3DCOLOR_ARGB(255, 240, 250, 0), txtBuffer, 0); - } -} - -#endif // USE_DEBUG - - //----------------------------------------------------------------------------------------------------------------------------- BOOL APIENTRY DllMain( HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) { @@ -1003,7 +894,7 @@ void CNGPCarMenu::HandleFrontEndEvents(char txtKeyboard, bool bUp, bool bDown, b // Set a flag that custom replay generation is active during replays. If this is zero then replay plays the file normally m_iCustomReplayState = 1; - ::RBRAPI_Replay(C_REPLAYFILENAME_SCREENSHOT); + ::RBRAPI_Replay(this->m_sRBRRootDir, C_REPLAYFILENAME_SCREENSHOT); } else m_sMenuStatusText1 = "All cars already have a preview image. Did not create any new images."; @@ -1255,7 +1146,7 @@ HRESULT __fastcall CustomRBRDirectXBeginScene(void* objPointer) g_pRBRPlugin->m_iCustomReplayState = 1; } - RBRAPI_Replay(C_REPLAYFILENAME_SCREENSHOT); + RBRAPI_Replay(g_pRBRPlugin->m_sRBRRootDir, C_REPLAYFILENAME_SCREENSHOT); } } } diff --git a/src/NGPCarMenu.h b/src/NGPCarMenu.h index ce4cee1..782bdd7 100644 --- a/src/NGPCarMenu.h +++ b/src/NGPCarMenu.h @@ -31,15 +31,6 @@ //#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers //#include - -#ifdef _DEBUG -#define USE_DEBUG 1 // 1=Custom debug outputs and calculations (spends few cycles of precious CPU time), 0=No custom debugging, run the game code without extra debug code -#endif - -#ifndef _DEBUG -#undef USE_DEBUG -#endif - //#include //#include #include @@ -73,20 +64,6 @@ enum class T_PLUGINSTATE }; -//------------------------------------------------------------------------------------------------ - -#if USE_DEBUG == 1 -extern void DebugPrintFunc(LPCSTR lpszFormat, ...); -#endif - -#if USE_DEBUG == 1 - #define DebugPrint DebugPrintFunc // DEBUG version to dump logfile messages -#else - #define DebugPrint // RELEASE version of DebugPrint doing nothing -#endif - - - //------------------------------------------------------------------------------------------------ #define MAX_CARMENUNAME_NORMALCHARS 28 diff --git a/src/NGPCarMenu.ini b/src/NGPCarMenu.ini new file mode 100644 index 0000000..d1b7337 --- /dev/null +++ b/src/NGPCarMenu.ini @@ -0,0 +1,91 @@ +; +; NGPCarMenu - Plugin for Richard Burns Rally game (1.02 SSE RBR version) to generate car preview images (the actual custom 3D model view in "Select Car" menu) +; and to show car specs from the current NGP physics model information (NGP specs, longer car model names in RBR menus). +; +; https://github.com/mika-n/NGPCarMenu +; +; The original "Select Car" menu in RBR shows preview images and specs of the stock cars. These has nothing to do +; with custom NGP physics (car specs) and 3D car models. This plugin shows the correct preview image and specs for those people +; who use RBR menus to start a race and car (ie. don't use 3rd party launchers). +; +; Use "Options/Plugins/NGPCarMenu/Create car images" game menu to generate new car preview images (PNG format) if the existing +; configured NGP car model doesn't have an image in "Select Car" race menu. +; +; The plugin uses the information from configuration files of RBRCIT tool to read correct car specs (https://github.com/zissakos/RBRCIT). +; You should download and install RBRCIT tool to manage NGP car physics and custom 3D car models, because otherwise this plugin +; cannot find car specifications information (engine, gears, wheels, horsepower, year, FIA category, car model name, etc). +; +; CONFIGURATION OF NGPCarMenu: +; +; ScreenshotPath = Location of car preview images (relative to RBR.EXE location or absolute path). Resolution specific subfolder below this folder. +; ScreenshotReplay = Replay file this plugin uses to generate preview car images. +; ScreenshotCarPosition = Car position within the replay video while taking the car preview screenshot. (well, not yet implemented. Car position is set in the plugin) +; ScreenshotCamPosition = Camera position while taking the screenshot (well, not yet implemented. Cam position is set in the plugin) +; +; RBRCITCarListPath = Path to the carList.ini file of RBRCIT car manager tool (carList.ini contains NGP car spec information). +; +; ScreenshotCropping = Cropping rectangle for the screenshot (make it "big enough but not too big" to fill the bottom part of "Select Car" RBR menu). +; Value are in pixels "left top right bottom". +; (Pro tip. To find a good ScreenshotCropping rectangle coordinates, take a fullscreen screenshot of the car in +; replay video and then use MS-Paint app to find corner coordinates in pixels to fit the car in a rectangle area). +; +; CarSelectLeftBlackBar = Optional rectangle black bar to hide the stock RBR "left frame" image in "Select Car" menu (empty string or 0 0 0 0 removes black side bars). +; CarSelectRightBlackBar= (see above) +; +; The plugin supports resolution specific preview images. Feel free to add new resolution specific configuration entries if the one you use is missing. +; + +[Default] +FileFormat=1 +ScreenshotPath=Plugins\NGPCarMenu\preview +ScreenshotReplay=NGPCarMenu.rpl +ScreenshotCarPosition= +ScreenshotCamPosition= +RBRCITCarListPath=RBRCIT\carlist\carList.ini + +[1920x1080] +ScreenshotCropping=0 236 1920 780 +CarSelectLeftBlackBar=0 0 238 1080 +CarSelectRightBlackBar=1682 0 1920 1080 + +[1600x900] +ScreenshotCropping=0 220 1600 750 +CarSelectLeftBlackBar= +CarSelectRightBlackBar= + +[1366x768] +ScreenshotCropping=0 176 1366 550 +CarSelectLeftBlackBar=0 0 169 768 +CarSelectRightBlackBar=1198 0 1366 768 + +[1360x768] +ScreenshotCropping=0 176 1360 550 +CarSelectLeftBlackBar=0 0 166 768 +CarSelectRightBlackBar=1195 0 1360 768 + +[1280x1024] +ScreenshotCropping=0 320 1280 570 +CarSelectLeftBlackBar= +CarSelectRightBlackBar= + +[1280x800] +ScreenshotCropping=0 320 1280 570 +CarSelectLeftBlackBar= +CarSelectRightBlackBar= + +[1280x720] +ScreenshotCropping=0 320 1280 570 +CarSelectLeftBlackBar= +CarSelectRightBlackBar= + +[1024x768] +ScreenshotCropping=0 300 1024 600 +CarSelectLeftBlackBar= +CarSelectRightBlackBar= + +[640x480] +ScreenshotCropping=0 300 640 400 +CarSelectLeftBlackBar= +CarSelectRightBlackBar= + +; 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.rpl b/src/NGPCarMenu.rpl new file mode 100644 index 0000000..d70128a Binary files /dev/null and b/src/NGPCarMenu.rpl differ diff --git a/src/RBRAPI.cpp b/src/RBRAPI.cpp index 31ebff8..5f23316 100644 --- a/src/RBRAPI.cpp +++ b/src/RBRAPI.cpp @@ -42,6 +42,11 @@ #include "RBRAPI.h" +#ifdef _DEBUG +#include "D3D9Helpers.h" +#endif + + namespace fs = std::filesystem; //---------------------------------------------------------------------------------------------------------------------------- @@ -227,7 +232,7 @@ void RBRAPI_MapRBRPointToScreenPoint(const float srcX, const float srcY, float* *trgY = static_cast(srcY * (g_rectRBRWndClient.bottom / 480.0f /*g_pRBRGameConfig->resolutionY*/)); } -BOOL RBRAPI_Replay(LPCSTR szReplayFileName) +BOOL RBRAPI_Replay(const std::string rbrAppFolder, LPCSTR szReplayFileName) { // https://suxin.space/notes/rbr-play-replay/ (Sasha / Suxin) // RBR replay function call entry point to call it programmatically. Thanks Sasha for documenting this trick. @@ -238,9 +243,11 @@ BOOL RBRAPI_Replay(LPCSTR szReplayFileName) try { - if (fs::exists(szReplayFileName)) + std::string fullReplayFilePath = rbrAppFolder + "\\Replays\\" + szReplayFileName; + + if (fs::exists(fullReplayFilePath)) { - size_t iReplayFileSizeInBytes = (size_t) std::filesystem::file_size(szReplayFileName); + size_t iReplayFileSizeInBytes = (size_t) std::filesystem::file_size(fullReplayFilePath); func_RBRReplay(*objRBRThis, szReplayFileName, &iNotUsed, &iNotUsed, iReplayFileSizeInBytes); // TODO. Check error status if replay loading failed diff --git a/src/RBRAPI.h b/src/RBRAPI.h index 039383c..eaff4a3 100644 --- a/src/RBRAPI.h +++ b/src/RBRAPI.h @@ -90,7 +90,7 @@ extern BOOL WriteOpCodeBuffer(const LPVOID writeAddr, const BYTE* buffer, const extern BOOL WriteOpCodePtr(const LPVOID writeAddr, const LPVOID ptrValue); extern int RBRAPI_MapCarIDToMenuIdx(int carID); // 00..07 carID is not the same as order of car selection items -extern BOOL RBRAPI_Replay(LPCSTR szReplayFileName); +extern BOOL RBRAPI_Replay(const std::string rbrAppFolder, LPCSTR szReplayFileName); extern void RBRAPI_MapRBRPointToScreenPoint(const float srcX, const float srcY, int* trgX, int* trgY); extern void RBRAPI_MapRBRPointToScreenPoint(const float srcX, const float srcY, float* trgX, float* trgY);