diff --git a/BinaryObjectScanner/Protection/Roxxe.cs b/BinaryObjectScanner/Protection/Roxxe.cs
new file mode 100644
index 00000000..6f957487
--- /dev/null
+++ b/BinaryObjectScanner/Protection/Roxxe.cs
@@ -0,0 +1,83 @@
+using System;
+#if NET40_OR_GREATER || NETCOREAPP
+using System.Collections.Concurrent;
+#endif
+using System.Collections.Generic;
+using System.Linq;
+using BinaryObjectScanner.Interfaces;
+using SabreTools.Matching;
+using SabreTools.Serialization.Wrappers;
+
+namespace BinaryObjectScanner.Protection
+{
+ ///
+ /// Roxxe was a Czech DRM. It appears to have been a simple disc check that also relied on unusual disc manufacturing and dummy files to attempt to prevent copying.
+ ///
+ /// DRML: https://github.com/TheRogueArchivist/DRML/blob/main/entries/Roxxe/Roxxe.md
+ ///
+ public class Roxxe : IPathCheck, IPortableExecutableCheck
+ {
+ ///
+ public string? CheckPortableExecutable(string file, PortableExecutable pex, bool includeDebug)
+ {
+ // Get the sections from the executable, if possible
+ var sections = pex.Model.SectionTable;
+ if (sections == null)
+ return null;
+
+ // Get the code/CODE section strings, if they exist
+ var strs = pex.GetFirstSectionStrings("code") ?? pex.GetFirstSectionStrings("CODE");
+ if (strs != null)
+ {
+ // Found in "Owar.exe" in IA item "game4u-22-cd".
+ if (strs.Any(s => s.Contains("TRCHANGER.INI")))
+ return "Roxxe";
+ }
+
+ // Get the .rsrc section strings, if they exist
+ // TODO: Check for these strings specifically within the application-defined resource that they're found in, not just the generic resource section.
+ strs = pex.GetFirstSectionStrings(".rsrc");
+ if (strs != null)
+ {
+ // Found in "Owar.exe" in IA items "game4u-22-cd" and "original-war".
+ // These checks are less reliable, as they are still found in a version of the game that appears to have patched out Roxxe (the version present in IA item "original-war").
+ if (strs.Any(s => s.Contains("PRRT01")))
+ return "Roxxe (Possibly remnants)";
+
+ if (strs.Any(s => s.Contains("CommonPRRT")))
+ return "Roxxe (Possibly remnants)";
+
+ if (strs.Any(s => s.Contains("roxe")))
+ return "Roxxe (Possibly remnants)";
+ }
+
+ return null;
+ }
+
+ ///
+#if NET20 || NET35
+ public Queue CheckDirectoryPath(string path, IEnumerable? files)
+#else
+ public ConcurrentQueue CheckDirectoryPath(string path, IEnumerable? files)
+#endif
+ {
+ var matchers = new List
+ {
+ // Files such as "TRCHANGER.INI" may be present, but haven't been found yet.
+ };
+
+ return MatchUtil.GetAllMatches(files, matchers, any: true);
+ }
+
+ ///
+ public string? CheckFilePath(string path)
+ {
+ var matchers = new List
+ {
+ // Files such as "TRCHANGER.INI" may be present, but haven't been found yet.
+ };
+
+ return MatchUtil.GetFirstMatch(path, matchers, any: true);
+ }
+ }
+}
diff --git a/README.md b/README.md
index 45e96864..873b1447 100644
--- a/README.md
+++ b/README.md
@@ -94,6 +94,7 @@ Below is a list of protections detected by BinaryObjectScanner. The two columns
| Rainbow Sentinel | True | True | |
| Ring PROTECH / ProRing | True | True | Partially unconfirmed² |
| RipGuard | True | True | Partially unconfirmed² |
+| Roxxe | True | False | |
| SafeDisc / SafeCast | True | True | Can't distinguish between some versions of SafeDisc and SafeCast |
| SafeLock | False | True | |
| SecuROM | True | True | v8.x and White Label detected partially² |