Patch to fix cutscenes on Resident Evil titles

For those that have tried to play Resident Evil Mercenaries 3D or Resident Evil Revelations, you might have found that while the games play fine, the framerate drops into the low single digits during cutscenes. This is less of a problem for Mercenaries 3D since it only has one “cutscene” in the main menu, but makes Revelations completely unplayable.

I went digging around and found the changes somebody had made for an “FMV hack” option in a very old fork. I was able to adapt the changes to come up with a new patch that applies to the latest nightly build, and surprisingly it seemed perfectly stable. I was able to successfully play through all of Revelations without a single bug.

Of course, it is a hack, and I don’t even understand why it works, so I definitely can’t contribute it upstream. The patch adds an “FMV hack” option with a setting for “FMV tick”. Interestingly, you have to set this to wildly different values - 18000 for Revelations, but Mercenaries 3D needs ~2000. Anyway, here is the patch - hope this helps somebody else who wants to play Resident Evil on the 3DS.

diff --git a/src/citra_qt/configuration/config.cpp b/src/citra_qt/configuration/config.cpp
index 8901c47c2..ac3fa6a52 100644
--- a/src/citra_qt/configuration/config.cpp
+++ b/src/citra_qt/configuration/config.cpp
@@ -498,6 +498,9 @@ void Config::ReadRendererValues() {
             .toString()
             .toStdString();
 
+    Settings::values.FMV_hack = ReadSetting(QStringLiteral("FMV_hack"), false).toBool();
+    Settings::values.FMV_tick = ReadSetting(QStringLiteral("FMV_tick"), 18000ULL).toULongLong();
+
     qt_config->endGroup();
 }
 
@@ -1000,6 +1003,9 @@ void Config::SaveRendererValues() {
                  QString::fromStdString(Settings::values.texture_filter_name),
                  QStringLiteral("none"));
 
+    WriteSetting(QStringLiteral("FMV_hack"), Settings::values.FMV_hack, false);
+    WriteSetting(QStringLiteral("FMV_tick"), QVariant::fromValue(Settings::values.FMV_tick), QVariant::fromValue(18000ULL));
+
     qt_config->endGroup();
 }
 
diff --git a/src/citra_qt/configuration/configure_graphics.cpp b/src/citra_qt/configuration/configure_graphics.cpp
index 44290f783..530cdea6c 100644
--- a/src/citra_qt/configuration/configure_graphics.cpp
+++ b/src/citra_qt/configuration/configure_graphics.cpp
@@ -75,6 +75,8 @@ void ConfigureGraphics::SetConfiguration() {
     ui->toggle_shader_jit->setChecked(Settings::values.use_shader_jit);
     ui->toggle_disk_shader_cache->setChecked(Settings::values.use_disk_shader_cache);
     ui->toggle_vsync_new->setChecked(Settings::values.use_vsync_new);
+    ui->toggle_FMV_hack->setChecked(Settings::values.FMV_hack);
+    ui->edit_FMV_tick->setText(QString::number(Settings::values.FMV_tick));
 }
 
 void ConfigureGraphics::ApplyConfiguration() {
@@ -85,6 +87,8 @@ void ConfigureGraphics::ApplyConfiguration() {
     Settings::values.use_shader_jit = ui->toggle_shader_jit->isChecked();
     Settings::values.use_disk_shader_cache = ui->toggle_disk_shader_cache->isChecked();
     Settings::values.use_vsync_new = ui->toggle_vsync_new->isChecked();
+    Settings::values.FMV_hack = ui->toggle_FMV_hack->isChecked();
+    Settings::values.FMV_tick = ui->edit_FMV_tick->text().toULongLong();
 }
 
 void ConfigureGraphics::RetranslateUI() {
diff --git a/src/citra_qt/configuration/configure_graphics.ui b/src/citra_qt/configuration/configure_graphics.ui
index c9eb552a1..78d0f0cfe 100644
--- a/src/citra_qt/configuration/configure_graphics.ui
+++ b/src/citra_qt/configuration/configure_graphics.ui
@@ -109,6 +109,33 @@
         </property>
        </widget>
       </item>
+      <item>
+       <widget class="QCheckBox" name="toggle_FMV_hack">
+        <property name="text">
+	 <string>FMV Hack</string>
+	</property>
+       </widget>
+      </item>
+      <item column="1">
+       <widget class="QLabel" name="label_FMV_tick">
+        <property name="text">
+	 <string>FMV tick</string>
+	</property>
+       </widget>
+      </item>
+      <item column="0">
+       <widget class="QLineEdit" name="edit_FMV_tick">
+        <property name="sizePolicy">
+	 <sizepolicy hsizetype="Preferred" vsizetype="Fixed">
+	  <horstretch>0</horstretch>
+	  <verstretch>0</verstretch>
+	 </sizepolicy>
+	</property>
+	<property name="maxLength">
+	 <number>10</number>
+	</property>
+       </widget>
+      </item>
      </layout>
     </widget>
    </item>
diff --git a/src/core/core_timing.cpp b/src/core/core_timing.cpp
index 3d77932b6..d99f353c2 100644
--- a/src/core/core_timing.cpp
+++ b/src/core/core_timing.cpp
@@ -8,6 +8,7 @@
 #include "common/assert.h"
 #include "common/logging/log.h"
 #include "core/core_timing.h"
+#include "core/settings.h"
 
 namespace Core {
 
@@ -141,7 +142,8 @@ u64 Timing::Timer::GetTicks() const {
 }
 
 void Timing::Timer::AddTicks(u64 ticks) {
-    downcount -= static_cast<u64>(ticks * cpu_clock_scale);
+    const u64 tick_multiplier = Settings::values.FMV_hack ? Settings::values.FMV_tick : (ticks * cpu_clock_scale);
+    downcount -= tick_multiplier;
 }
 
 u64 Timing::Timer::GetIdleTicks() const {
diff --git a/src/core/settings.h b/src/core/settings.h
index 0876d312f..f79590e8b 100644
--- a/src/core/settings.h
+++ b/src/core/settings.h
@@ -188,6 +188,8 @@ struct Values {
     bool preload_textures;
 
     bool use_vsync_new;
+    bool FMV_hack;
+    u64 FMV_tick;
 
     // Audio
     bool enable_dsp_lle;

Underclocking Citra in the System tab would also speed up these cutscenes however it can cause freezes as it makes emulation unstable.

Yes, I originally underclocked to 25%, but could not get more than 5 minutes or so without a crash, so that’s not really a solution.

I did not observe any other issues with this patch through the entire playthrough, and it makes the setting optional so it can be disabled to revert to default behavior.

How do you apply this patch? Can anybody walk me through it?

I just tested and this still applies fine on top of master. Assuming you have this in fmv_hack.patch:

git clone --depth=1 https://github.com/citra-emu/citra
cd citra
patch -p1 < ../fmv_hack.patch

then proceed to build as normal.

I guarantee majority of people who stumble across this have no idea how to patch or build anything. Just the average user to knows how to download, set up and play an emulator and want to play Revelations without the headache of 2fps cutscenes

I tried myself and ended up with errors after much headaches.

Why not patch it and provide the compiled build for us? or create a fork on github? Or at the very least detailed instructions?

Because:

a) I ran this on Linux so I have no MSVC toolchain to create a Windows build,
b) Even if I did, you shouldn’t download random binaries off forum posts; the mods would be well within their rights to remove such an attachment, and
c) Forks and unofficial builds have the same problem, they rapidly become out of date. In the form of a patch, this continues to be usable on the latest nightly.

There are already detailed official instructions on building for Windows here, no need for me to duplicate that.