Files
OpenMaidEngine/engine/Age.Engine.Tests/AudioMixerOpcodeTests.cs
2026-08-03 13:28:59 -04:00

222 lines
8.5 KiB
C#
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using Age.Engine.Model;
using Age.Engine.Persistence;
using Age.Engine.Sys4;
using Age.Engine.Vm;
using System.Text;
public class AudioMixerOpcodeTests
{
private static readonly OpcodeTable Table = OpcodeTableJson.Load(Paths.OpcodesJson);
private static Operand G(int address) => new(3, address);
private static Operand I(long value) => new(0, value);
private static (int, Operand[]) Exit() => (0x2, Array.Empty<Operand>());
[Fact]
public void NativeDefaultsUseUnconfiguredVolumesAndEnabledRoutes()
{
var settings = new AudioMixerSettings();
for (int category = 0; category < AudioMixerSettings.CategoryCount; category++)
{
Assert.True(settings.TryGetVolume(category, out int volume));
Assert.Equal(AudioMixerSettings.UnconfiguredVolume, volume);
}
for (int category = (int)AudioMixerCategory.Music;
category < AudioMixerSettings.CategoryCount;
category++)
{
Assert.True(settings.TryGetRouteEnabled(category, out bool enabled));
Assert.True(enabled);
}
}
[Fact]
public void MixerOpcodesRoundTripCategoriesAndApplyLiveChanges()
{
var settings = new AudioMixerSettings();
var host = new RecordingHost();
Script scene = ScriptAssembler.Assemble(Table, "CONFIG_MIXER",
[
(0xc6, [I(0), I(8000)]),
(0xc6, [I(3), I(4500)]),
(0x1ba, [I(1), I(0)]),
(0xc5, [I(0), G(10)]),
(0xc5, [I(3), G(11)]),
(0xc5, [I(2), G(12)]),
(0xc7, [I(1), G(13)]),
(0xc7, [I(2), G(14)]),
Exit(),
], []);
var vm = new VirtualMachine(scene, Table, host, audioMixerSettings: settings);
vm.Run();
Assert.Equal(8000, vm.Globals[10]);
Assert.Equal(4500, vm.Globals[11]);
Assert.Equal(AudioMixerSettings.UnconfiguredVolume, vm.Globals[12]);
Assert.Equal(0, vm.Globals[13]);
Assert.Equal(1, vm.Globals[14]);
Assert.Equal([(0, 8000), (3, 4500)], host.AudioVolumeChanges);
Assert.Equal([(1, false)], host.AudioRouteChanges);
}
[Fact]
public void RouteSetterIsIdempotentLikeNativeWorkers()
{
var host = new RecordingHost();
Script scene = ScriptAssembler.Assemble(Table, "CONFIG_ROUTE",
[
(0x1ba, [I(2), I(0)]),
(0x1ba, [I(2), I(0)]),
(0x1ba, [I(2), I(1)]),
(0x1ba, [I(2), I(1)]),
Exit(),
], []);
new VirtualMachine(scene, Table, host).Run();
Assert.Equal([(2, false), (2, true)], host.AudioRouteChanges);
}
[Fact]
public void GameSessionCarriesMixerStateAcrossFreshSceneVms()
{
var session = new GameSession();
Script setter = ScriptAssembler.Assemble(Table, "CONFIG_SET",
[
(0xc6, [I(2), I(6250)]),
(0x1ba, [I(3), I(0)]),
Exit(),
], []);
Script getter = ScriptAssembler.Assemble(Table, "CONFIG_GET",
[
(0xc5, [I(2), G(20)]),
(0xc7, [I(3), G(21)]),
Exit(),
], []);
session.RunScene(setter, Table, new RecordingHost());
session.RunScene(getter, Table, new RecordingHost());
Assert.Equal(6250, session.Globals[20]);
Assert.Equal(0, session.Globals[21]);
}
[Fact]
public void NativeSys4RegIniRoundTripsAudioAndPreservesOtherOptions()
{
string directory = Path.Combine(Path.GetTempPath(), $"age-audio-settings-{Guid.NewGuid():N}");
string path = Path.Combine(directory, Sys4RegIniStore.FileName);
try
{
Directory.CreateDirectory(directory);
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
Encoding cp932 = Encoding.GetEncoding(932);
const string original =
"[display]\r\nScreenMode=1\r\n\r\n" +
"[sound]\r\nSound=1\r\nMusic=2\r\nSE=7\r\nVoice=1\r\nMovie=1\r\n" +
"Volume0=5500\r\nVolume1=2500\r\nVolume2=3500\r\nVolume3=3500\r\nVolume4=-1\r\n" +
"UseDirectSound=1\r\n\r\n" +
"[message]\r\nFont= 明朝\r\nMessageSpeed=50\r\n";
File.WriteAllText(path, original, cp932);
var store = new Sys4RegIniStore(path, defaultMusicRouteValue: 2);
AudioMixerSettings settings = store.Load();
settings.Changed += store.Save;
Assert.True(settings.TrySetVolume((int)AudioMixerCategory.Master, 9000));
Assert.True(settings.TrySetVolume((int)AudioMixerCategory.Movie, 3750));
Assert.True(settings.TrySetRouteEnabled(
(int)AudioMixerCategory.Voice, false, out bool changed));
Assert.True(changed);
Assert.True(settings.TrySetRouteEnabled(
(int)AudioMixerCategory.Music, false, out changed));
Assert.True(changed);
Assert.Contains("Music=-1\r\n", File.ReadAllText(path, cp932));
Assert.True(settings.TrySetRouteEnabled(
(int)AudioMixerCategory.Music, true, out changed));
Assert.True(changed);
AudioMixerSettings loaded = store.Load();
string rewritten = File.ReadAllText(path, cp932);
Assert.True(loaded.TryGetVolume((int)AudioMixerCategory.Master, out int master));
Assert.True(loaded.TryGetVolume((int)AudioMixerCategory.Movie, out int movie));
Assert.True(loaded.TryGetRouteEnabled((int)AudioMixerCategory.Voice, out bool voice));
Assert.Equal(9000, master);
Assert.Equal(3750, movie);
Assert.False(voice);
Assert.Contains("[display]\r\nScreenMode=1\r\n", rewritten);
Assert.Contains("Sound=1\r\n", rewritten);
Assert.Contains("Music=2\r\n", rewritten);
Assert.Contains("SE=7\r\n", rewritten);
Assert.Contains("UseDirectSound=1\r\n", rewritten);
Assert.Contains("[message]\r\nFont= 明朝\r\nMessageSpeed=50\r\n", rewritten);
Assert.DoesNotContain("engine-settings", rewritten);
}
finally
{
if (Directory.Exists(directory)) Directory.Delete(directory, recursive: true);
}
}
[Fact]
[Trait("Category", "Workspace")]
public void NativePersistencePathsComeFromIndependentSys4IniProfileValues()
{
Sys4AssetCatalog catalog = Sys4AssetCatalog.Load(Paths.Sys4Ini);
string localAppData = Path.Combine(Path.GetTempPath(), "native-appdata-root");
Sys4PersistencePaths paths = Sys4PersistencePaths.ResolveNative(
catalog.StartupSettings, "C:\\unused-game-root", localAppData);
Assert.Equal(
Path.Combine(localAppData, "Eushully", "姫狩りダンジョンマイスター", "SAVE"),
paths.SaveDirectory);
Assert.Equal(
Path.Combine(localAppData, "Eushully", "姫狩りダンジョンマイスター", "SYS4REG.INI"),
paths.Sys4RegIniPath);
}
[Fact]
[Trait("Category", "Workspace")]
public void ProfileOverrideRedirectsSaveAndSettingsAsOneNativeLayout()
{
Sys4AssetCatalog catalog = Sys4AssetCatalog.Load(Paths.Sys4Ini);
string profileRoot = Path.Combine(Path.GetTempPath(), "redirected-profile-root");
Sys4PersistencePaths paths = Sys4PersistencePaths.ResolveProfileOverride(
catalog.StartupSettings, profileRoot);
Sys4RegIniStore store = Sys4RegIniStore.ForPath(
catalog.StartupSettings, paths.Sys4RegIniPath);
Assert.Equal(Path.Combine(profileRoot, "SAVE"), paths.SaveDirectory);
Assert.Equal(Path.Combine(profileRoot, "SYS4REG.INI"), paths.Sys4RegIniPath);
Assert.Equal(paths.Sys4RegIniPath, store.FilePath);
}
[Fact]
public void InvalidMixerCategoriesWarnAndLeaveOutputsUntouched()
{
var host = new RecordingHost();
Script scene = ScriptAssembler.Assemble(Table, "CONFIG_INVALID",
[
(0xc5, [I(5), G(30)]),
(0xc6, [I(-1), I(5000)]),
(0xc7, [I(0), G(31)]),
(0x1ba, [I(5), I(1)]),
Exit(),
], []);
var vm = new VirtualMachine(scene, Table, host);
vm.Globals[30] = 123;
vm.Globals[31] = 456;
vm.Run();
Assert.Equal(123, vm.Globals[30]);
Assert.Equal(456, vm.Globals[31]);
Assert.Equal(4, host.Warnings.Count);
Assert.Empty(host.AudioVolumeChanges);
Assert.Empty(host.AudioRouteChanges);
}
}