From 7d0e3e8da52b88cd7e3b0566da359120c0afdf5f Mon Sep 17 00:00:00 2001
From: FunkyFr3sh <cc.red.alert.1@googlemail.com>
Date: Sun, 18 Oct 2020 02:40:45 +0200
Subject: [PATCH] replace "forcefps=" with "minfps="

---
 inc/dd.h          |  3 ++-
 src/config.c      | 48 +++++++++++++++++++++++++++--------------------
 src/render_d3d9.c |  4 +++-
 src/render_gdi.c  |  4 +++-
 src/render_ogl.c  |  4 +++-
 5 files changed, 39 insertions(+), 24 deletions(-)

diff --git a/inc/dd.h b/inc/dd.h
index 01abeac..fb2ef4e 100644
--- a/inc/dd.h
+++ b/inc/dd.h
@@ -54,7 +54,8 @@ typedef struct cnc_ddraw
     struct
     {
         int maxfps;
-        BOOL forcefps;
+        int minfps;
+        DWORD minfps_tick_len;
         int width;
         int height;
         int bpp;
diff --git a/src/config.c b/src/config.c
index 443ff76..c296467 100644
--- a/src/config.c
+++ b/src/config.c
@@ -63,9 +63,17 @@ void cfg_load()
 #endif
     
     g_ddraw->render.maxfps = cfg_get_int("maxfps", 60);
+    g_ddraw->render.minfps = cfg_get_int("minfps", 0);
 
-    if (g_ddraw->render.maxfps)
-        g_ddraw->render.forcefps = cfg_get_bool("forcefps", FALSE);
+    if (g_ddraw->render.minfps > 1000)
+    {
+        g_ddraw->render.minfps = 1000;
+    }
+
+    if (g_ddraw->render.minfps > 0)
+    {
+        g_ddraw->render.minfps_tick_len = 1000.0f / g_ddraw->render.minfps;
+    }
 
     if (g_ddraw->accurate_timers || g_ddraw->vsync)
         g_ddraw->fps_limiter.htimer = CreateWaitableTimer(NULL, TRUE, NULL);
@@ -295,12 +303,13 @@ static void cfg_create_ini()
             "handlemouse=true\n"
             "\n"
             "; Windows API Hooking, Possible values: 0 = disabled, 1 = IAT Hooking, 2 = Microsoft Detours, 3 = IAT+Detours Hooking (All Modules), 4 = IAT Hooking (All Modules)\n"
-            "; Note: Can be used to fix issues related to new features added by cnc-ddraw such as windowed mode or stretching\n"
+            "; Note: Change this value if windowed mode or upscaling isn't working properly\n"
+            "; Note: 'hook=2' will usually work for problematic games, but 'hook=2' must be combined with renderer=gdi\n"
             "hook=4\n"
             "\n"
-            "; Force consistent FPS (Requires 'maxfps=' to be set to a value other than 0)\n"
-            "; Note: Fixes flickering cursor issues in C&C games (Might be useful for some screen recorders too)\n"
-            "forcefps=false\n"
+            "; Force minimum FPS, possible values: 0 = disabled, -1 = use 'maxfps=' value, 1-1000 = custom FPS\n"
+            "; Note: Set this to a low value such as 5 or 10 if some parts of the game are not being displayed (e.g. menus or loading screens)\n"
+            "minfps=0\n"
             "\n"
             "; Disable fullscreen-exclusive mode for the OpenGL renderer\n"
             "; Note: Can be used in case some GUI elements like buttons/textboxes/videos/etc.. are invisible\n"
@@ -339,18 +348,18 @@ static void cfg_create_ini()
             "[C&C95]\n"
             "maxgameticks=120\n"
             "maxfps=60\n"
-            "forcefps=true\n"
+            "minfps=-1\n"
             "\n"
             "; Command & Conquer: Red Alert\n"
             "[ra95]\n"
             "maxgameticks=120\n"
             "maxfps=60\n"
-            "forcefps=true\n"
+            "minfps=-1\n"
             "\n"
             "; Command & Conquer: Red Alert\n"
             "[ra95p]\n"
             "maxfps=60\n"
-            "forcefps=true\n"
+            "minfps=-1\n"
             "\n"
             "; Age of Empires\n"
             "[empires]\n"
@@ -413,71 +422,70 @@ static void cfg_create_ini()
             "noactivateapp=true\n"
             "handlemouse=false\n"
             "maxfps=60\n"
-            "forcefps=true\n"
+            "minfps=-1\n"
             "\n"
             "; Command & Conquer: Tiberian Sun Demo\n"
             "[SUN]\n"
             "noactivateapp=true\n"
             "handlemouse=false\n"
             "maxfps=60\n"
-            "forcefps=true\n"
+            "minfps=-1\n"
             "\n"
             "; Command & Conquer: Tiberian Sun - CnCNet\n"
             "[ts-spawn]\n"
             "noactivateapp=true\n"
             "handlemouse=false\n"
             "maxfps=60\n"
-            "forcefps=true\n"
+            "minfps=-1\n"
             "\n"
             "; Command & Conquer: Red Alert 2 - XWIS\n"
             "[ra2]\n"
             "noactivateapp=true\n"
             "handlemouse=false\n"
             "maxfps=60\n"
-            "forcefps=true\n"
+            "minfps=-1\n"
             "\n"
             "; Command & Conquer: Red Alert 2 - XWIS\n"
             "[Red Alert 2]\n"
             "noactivateapp=true\n"
             "handlemouse=false\n"
             "maxfps=60\n"
-            "forcefps=true\n"
+            "minfps=-1\n"
             "\n"
             "; Command & Conquer: Red Alert 2: Yuri's Revenge\n"
             "[gamemd]\n"
             "noactivateapp=true\n"
             "handlemouse=false\n"
             "maxfps=60\n"
-            "forcefps=true\n"
+            "minfps=-1\n"
             "\n"
             "; Command & Conquer: Red Alert 2: Yuri's Revenge - ?ModExe?\n"
             "[ra2md]\n"
             "noactivateapp=true\n"
             "handlemouse=false\n"
             "maxfps=60\n"
-            "forcefps=true\n"
+            "minfps=-1\n"
             "\n"
             "; Command & Conquer: Red Alert 2: Yuri's Revenge - CnCNet\n"
             "[gamemd-spawn]\n"
             "noactivateapp=true\n"
             "handlemouse=false\n"
             "maxfps=60\n"
-            "forcefps=true\n"
+            "minfps=-1\n"
             "\n"
             "; Command & Conquer: Red Alert 2: Yuri's Revenge - XWIS\n"
             "[Yuri's Revenge]\n"
             "noactivateapp=true\n"
             "handlemouse=false\n"
             "maxfps=60\n"
-            "forcefps=true\n"
+            "minfps=-1\n"
             "\n"
             "; Twisted Metal\n"
             "[TWISTED]\n"
             "renderer=opengl\n"
             "nonexclusive=true\n"
             "maxgameticks=25\n"
-            "maxfps=25\n"
-            "forcefps=true\n"
+            "minfps=5\n"
             "\n"
             "; Twisted Metal 2\n"
             "[Tm2]\n"
diff --git a/src/render_d3d9.c b/src/render_d3d9.c
index 9bfe54e..9ffaaa0 100644
--- a/src/render_d3d9.c
+++ b/src/render_d3d9.c
@@ -319,8 +319,10 @@ DWORD WINAPI d3d9_render_main(void)
     DWORD tick_end = 0;
     BOOL needs_update = FALSE;
 
+    DWORD timeout = g_ddraw->render.minfps > 0 ? g_ddraw->render.minfps_tick_len : 200;
+
     while (g_ddraw->render.run && 
-        (g_ddraw->render.forcefps || WaitForSingleObject(g_ddraw->render.sem, 200) != WAIT_FAILED))
+        (g_ddraw->render.minfps < 0 || WaitForSingleObject(g_ddraw->render.sem, timeout) != WAIT_FAILED))
     {
 #if _DEBUG
         dbg_draw_frame_info_start();
diff --git a/src/render_gdi.c b/src/render_gdi.c
index e1c96b7..7ed0ba6 100644
--- a/src/render_gdi.c
+++ b/src/render_gdi.c
@@ -49,8 +49,10 @@ DWORD WINAPI gdi_render_main(void)
         g_ddraw->fps_limiter.tick_length = len + (g_ddraw->accurate_timers ? 0.5f : 0.0f);
     }
 
+    DWORD timeout = g_ddraw->render.minfps > 0 ? g_ddraw->render.minfps_tick_len : INFINITE;
+
     while (g_ddraw->render.run &&
-        (g_ddraw->render.forcefps || WaitForSingleObject(g_ddraw->render.sem, INFINITE) != WAIT_FAILED))
+        (g_ddraw->render.minfps < 0 || WaitForSingleObject(g_ddraw->render.sem, timeout) != WAIT_FAILED))
     {
 #if _DEBUG
         dbg_draw_frame_info_start();
diff --git a/src/render_ogl.c b/src/render_ogl.c
index 188d0f4..1da92ce 100644
--- a/src/render_ogl.c
+++ b/src/render_ogl.c
@@ -547,8 +547,10 @@ static void ogl_render()
         glEnable(GL_TEXTURE_2D);
     }
 
+    DWORD timeout = g_ddraw->render.minfps > 0 ? g_ddraw->render.minfps_tick_len : INFINITE;
+
     while (g_ogl.use_opengl && g_ddraw->render.run &&
-        (g_ddraw->render.forcefps || WaitForSingleObject(g_ddraw->render.sem, INFINITE) != WAIT_FAILED))
+        (g_ddraw->render.minfps < 0 || WaitForSingleObject(g_ddraw->render.sem, timeout) != WAIT_FAILED))
     {
 #if _DEBUG
         dbg_draw_frame_info_start();