From 9d3cd673ac89fd72364ef7cd4b3d5be38e782f41 Mon Sep 17 00:00:00 2001 From: cre8ive-marketing <168519979+cre8ive-marketing@users.noreply.github.com> Date: Sat, 24 Oct 2026 01:23:29 +0530 Subject: [PATCH 1/2] Read CRI USM game videos (.usm) (#485) USM is CRI Middleware's container for game cutscenes. The bundled FFmpeg (9.0.2) has its usm demuxer, so .usm is a read-only video source like the other read-only containers: on the video donut, to MP4 and every other video and audio target. Encrypted USM files fail with FFmpeg's error. Settings v43: existing libraries learn .usm on the video donut, custom claims stay put. --- docs/ENGINES.md | 3 +++ src/Filee.Core/Formats/FormatRegistry.cs | 2 ++ src/Filee.Core/Settings/AppSettings.cs | 2 +- src/Filee.Core/Settings/UserDataStore.cs | 3 +++ tests/Filee.Core.Tests/UsmFormatTests.cs | 34 ++++++++++++++++++++++++ tests/Filee.Engines.Tests/MediaTests.cs | 10 +++++++ 6 files changed, 53 insertions(+), 1 deletion(-) create mode 100644 tests/Filee.Core.Tests/UsmFormatTests.cs diff --git a/docs/ENGINES.md b/docs/ENGINES.md index 355148efa..49808b882 100644 --- a/docs/ENGINES.md +++ b/docs/ENGINES.md @@ -1374,6 +1374,9 @@ streams without a container: FFmpeg reports them as format `hevc` / `h264` (`Med encoded, never copied, because their frames carry no timestamps (a copy into MP4 had non-monotonic DTS). A stream without timing information is read at FFmpeg's default 25 fps, so a camera that recorded at another rate plays faster or slower; its frames are all kept. +CRI USM (`.usm`, read only, #485), the video of many games' cutscenes, is read by FFmpeg's `usm` demuxer and goes to +every video and audio target like the other read-only containers. Encrypted USM files (common in recent games) can't +be read and fail with FFmpeg's error. A Video CD's `MPEGAV/AVSEQ01.DAT` (MPEG-1 in a RIFF/CDXA wrapper) counts as MPEG: `.dat` is an extension of that format, and FFmpeg reads what the file holds, so a `.dat` with no video in it fails instead of making a file. diff --git a/src/Filee.Core/Formats/FormatRegistry.cs b/src/Filee.Core/Formats/FormatRegistry.cs index 28ccc18b7..dc1ffac17 100644 --- a/src/Filee.Core/Formats/FormatRegistry.cs +++ b/src/Filee.Core/Formats/FormatRegistry.cs @@ -221,6 +221,8 @@ public static class FormatRegistry new("swf", "SWF", FormatCategory.Video, ["swf"], Writable: false), new("wtv", "WTV", FormatCategory.Video, ["wtv", "dvr-ms", "dvr"], Writable: false), new("cavs", "CAVS", FormatCategory.Video, ["cavs"], Writable: false), + // CRI Middleware's video for game cutscenes (#485): FFmpeg's usm demuxer reads unencrypted ones. + new("usm", "USM", FormatCategory.Video, ["usm"], Writable: false), // Raw H.265 / H.264 streams with no container, as security cameras and VMS software export them (issue #301). new("h265", "H.265", FormatCategory.Video, ["h265", "265", "hevc"], Writable: false), new("h264", "H.264", FormatCategory.Video, ["h264", "264"], Writable: false), diff --git a/src/Filee.Core/Settings/AppSettings.cs b/src/Filee.Core/Settings/AppSettings.cs index 91859a714..65c287f49 100644 --- a/src/Filee.Core/Settings/AppSettings.cs +++ b/src/Filee.Core/Settings/AppSettings.cs @@ -123,7 +123,7 @@ public static bool IsFlipKeyAllowed(string key, IEnumerable gest public sealed class AppSettings { /// Current schema version. Increase when the format changes and add a migration. - public const int CurrentSchemaVersion = 42; + public const int CurrentSchemaVersion = 43; public int SchemaVersion { get; set; } = CurrentSchemaVersion; diff --git a/src/Filee.Core/Settings/UserDataStore.cs b/src/Filee.Core/Settings/UserDataStore.cs index 593fe02b8..26880f438 100644 --- a/src/Filee.Core/Settings/UserDataStore.cs +++ b/src/Filee.Core/Settings/UserDataStore.cs @@ -836,6 +836,9 @@ public static bool ApplyToLibrary(int from, List presets, List p.Id == "video"); + video.Extensions.Remove("usm"); + + Assert.True(SettingsMigrations.ApplyToLibrary(42, presets, profiles)); + + Assert.Contains("usm", video.Extensions); + Assert.False(SettingsMigrations.ApplyToLibrary(42, presets, profiles)); + } +} diff --git a/tests/Filee.Engines.Tests/MediaTests.cs b/tests/Filee.Engines.Tests/MediaTests.cs index 51fc46e38..6035f1ade 100644 --- a/tests/Filee.Engines.Tests/MediaTests.cs +++ b/tests/Filee.Engines.Tests/MediaTests.cs @@ -23,6 +23,16 @@ public sealed class FfmpegCollection; [Collection(nameof(FfmpegCollection))] public class MediaTests(EngineFixture fx) : IClassFixture { + [Fact] + public void Cri_usm_game_videos_go_to_mp4_through_ffmpeg() + { + // #485: read only, through FFmpeg's usm demuxer; never a step towards something else. + var route = fx.Catalog.CreatePlanner().Plan("usm", "mp4"); + Assert.NotNull(route); + Assert.Equal("ffmpeg", Assert.Single(route.Steps).Converter.Id); + Assert.Null(fx.Catalog.CreatePlanner().Plan("mp4", "usm")); + } + private static readonly (string Ffmpeg, string Ffprobe)? Tools = FfmpegConverter.Locate(); private static void SkipUnlessInstalled() => From 8fd7da98e7a298548c9ea86fc06fad4656f5b69d Mon Sep 17 00:00:00 2001 From: cre8ive-marketing <168519979+cre8ive-marketing@users.noreply.github.com> Date: Sat, 24 Oct 2026 02:18:11 +0530 Subject: [PATCH 2/2] USM route test: skip where FFmpeg isn't installed, like the other media tests --- tests/Filee.Engines.Tests/MediaTests.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/Filee.Engines.Tests/MediaTests.cs b/tests/Filee.Engines.Tests/MediaTests.cs index 6035f1ade..0bfe086f4 100644 --- a/tests/Filee.Engines.Tests/MediaTests.cs +++ b/tests/Filee.Engines.Tests/MediaTests.cs @@ -27,6 +27,7 @@ public class MediaTests(EngineFixture fx) : IClassFixture public void Cri_usm_game_videos_go_to_mp4_through_ffmpeg() { // #485: read only, through FFmpeg's usm demuxer; never a step towards something else. + SkipUnlessInstalled(); var route = fx.Catalog.CreatePlanner().Plan("usm", "mp4"); Assert.NotNull(route); Assert.Equal("ffmpeg", Assert.Single(route.Steps).Converter.Id);