diff --git a/ScummVmBrowser/CLIScumm/ScummGameService.cpp b/ScummVmBrowser/CLIScumm/ScummGameService.cpp index dbbe458a31c7..606977e873e9 100644 --- a/ScummVmBrowser/CLIScumm/ScummGameService.cpp +++ b/ScummVmBrowser/CLIScumm/ScummGameService.cpp @@ -24,7 +24,8 @@ ScummWeb::ScummService::Marshalling::ScummGameService::ScummGameService(IConfigu soundOptions.restAudioTime = configureStore->GetValue(SoundSettings::RestAudioTime); NativeScummWrapperOptions nativeScummWrapperOptions; - nativeScummWrapperOptions.ScreenBufferCacheSize = configureStore->GetValue(CliScummSettings::ScreenBufferCacheSize); + nativeScummWrapperOptions.screenBufferCacheSize = configureStore->GetValue(CliScummSettings::ScreenBufferCacheSize); + nativeScummWrapperOptions.noBuffersBeforeWholeScreen = configureStore->GetValue(CliScummSettings::NoBuffersBeforeWholeScreen); _paletteManager = new NativeScummWrapperPaletteManager(); _graphics = new NativeScummWrapperGraphics(static_cast(Marshal::GetFunctionPointerForDelegate(imageUpdated).ToPointer()), _paletteManager, nativeScummWrapperOptions); diff --git a/ScummVmBrowser/ChunkedSoundManager/SoundThreadManager.cpp b/ScummVmBrowser/ChunkedSoundManager/SoundThreadManager.cpp index ff0548909028..ecc0d79ce9bc 100644 --- a/ScummVmBrowser/ChunkedSoundManager/SoundThreadManager.cpp +++ b/ScummVmBrowser/ChunkedSoundManager/SoundThreadManager.cpp @@ -57,7 +57,7 @@ void ScummWeb::ScummService::Sound::SoundThreadManager::StartSound() { //TODO: Log } - std::chrono::milliseconds sleepAmount = std::chrono::milliseconds(_soundOptions.soundPollingFrequencyMs); + std::chrono::milliseconds sleepAmount = std::chrono::milliseconds(_soundOptions.soundPollingFrequencyMs); if (_soundIsResting) { WaitForSingleObject(_stopSoundMutex, INFINITE); if (_soundIsResting) { @@ -67,6 +67,11 @@ void ScummWeb::ScummService::Sound::SoundThreadManager::StartSound() { ReleaseSemaphore(_stopSoundMutex, 1, NULL); } + WaitForSingleObject(_stopSoundMutex, INFINITE); + sleepAmount = std::chrono::milliseconds(_soundOptions.restAudioTime); + _soundIsResting = false; + ReleaseSemaphore(_stopSoundMutex, 1, NULL); + std::this_thread::sleep_for(sleepAmount); } }); diff --git a/ScummVmBrowser/DotNetScummTests/FrameCaptureTests.cs b/ScummVmBrowser/DotNetScummTests/FrameCaptureTests.cs index 33c40472bac7..5c36d0548f8e 100644 --- a/ScummVmBrowser/DotNetScummTests/FrameCaptureTests.cs +++ b/ScummVmBrowser/DotNetScummTests/FrameCaptureTests.cs @@ -46,10 +46,12 @@ public abstract class FrameCaptureTests public const string KingsQuest4OnMountain = "KingsQuest4OnMountain"; public const string KingsQuest6BeforeBeautySpeaks = "KingsQuest6BeforeBeautySpeaks"; public const string Kq5CanStart = "Kq5CanStart"; + public const string SteelDoomed = "SteelDoomed"; + public const string Kq3JustOutsideOffice = "Kq3JustOutsideOffice"; protected static ResourceManager ResourceManager = DotNetScummTests.Properties.Resources.ResourceManager; private object _clearLock; - + private int _noCaptured; Bitmap _b; @@ -68,6 +70,7 @@ protected FrameCaptureTests() _screenBuffers = new Dictionary(); _screenBuffersAddOrder = new Queue(); _configStore = new JsonConfigStore(); + _noCaptured = 0; } protected void InitBitmapByGame(AvailableGames game) @@ -160,6 +163,7 @@ public bool AssertThatExpectedFrameIsSeen(string expectedResourceName) bool arePicturesEqual = ArePicturesEqual(expectedBitmap, actualBitmap); expectedFrameFound = arePicturesEqual; } + _capturedFrames = new ConcurrentQueue(); return expectedFrameFound; } } @@ -174,16 +178,23 @@ protected bool ArePicturesEqual(string expectedBitmapName, Bitmap actualBitmap) private static bool ArePicturesEqual(Bitmap expectedBitmap, Bitmap actualBitmap) { - bool frameCouldBeExpected = true; - for (int x = 0; x < actualBitmap.Size.Width && frameCouldBeExpected; x++) + float diff = 0; + + for (int y = 0; y < expectedBitmap.Height; y++) { - for (int y = 0; y < actualBitmap.Size.Height && frameCouldBeExpected; y++) + for (int x = 0; x < expectedBitmap.Width; x++) { - frameCouldBeExpected = expectedBitmap.GetPixel(x, y) == actualBitmap.GetPixel(x, y); + Color pixel1 = expectedBitmap.GetPixel(x, y); + Color pixel2 = actualBitmap.GetPixel(x, y); + + if(pixel1.R != pixel2.R || pixel1.G != pixel2.G || pixel1.B != pixel2.B) + { + diff++; + } } } - return frameCouldBeExpected; + return (diff / (expectedBitmap.Width * expectedBitmap.Height * 3)) * 100 <= 5; } protected async Task WaitForFrame(int frame) @@ -204,7 +215,7 @@ protected async Task WaitAdditionalFrames(int additionalFrames, bool soundOff = } } - protected virtual void CaptureAndQuitWholeFrame(List screenBuffers, int noFrames, string expectedFrameName) + protected virtual void CaptureAndQuitWholeFrame(List screenBuffers, string expectedFrameName) { _b = new Bitmap(DisplayDefaultWidth, DisplayDefaultHeight); screenBuffers.Where(b => b.PaletteBuffer != null).ToList().ForEach(b => UpdatePalettes(b.PaletteHash, b.PaletteBuffer)); @@ -219,14 +230,14 @@ protected virtual void CaptureAndQuitWholeFrame(List screenBuffers } - CaptureAndQuit(_screenBuffers[screenBuffer.ScreenBufferHash], screenBuffer.IgnoreColour, screenBuffer.PaletteHash, screenBuffer.X, screenBuffer.Y, screenBuffer.W, screenBuffer.H, noFrames, expectedFrameName); + CaptureAndQuit(_screenBuffers[screenBuffer.ScreenBufferHash], screenBuffer.IgnoreColour, screenBuffer.PaletteHash, screenBuffer.X, screenBuffer.Y, screenBuffer.W, screenBuffer.H, expectedFrameName); } } - protected unsafe virtual void CaptureAndQuit(byte[] picBuff, int ignoreColour, UInt32 paletteHash, int x, int y, int w, int h, int noFrames, string expectedFrameName) + protected unsafe virtual void CaptureAndQuit(byte[] picBuff, int ignoreColour, UInt32 paletteHash, int x, int y, int w, int h, string expectedFrameName) { SetBitmapData(picBuff, _b, ignoreColour, paletteHash, x, y, w, h); - CaptureAndQuit(_b, noFrames, expectedFrameName); + CaptureAndQuit(_b, expectedFrameName); } protected unsafe void SetBitmapData(byte[] picBuff, Bitmap bitmap, int ignoreColour, uint paletteHash, int x, int y, int w, int h) @@ -274,28 +285,23 @@ protected unsafe void SetBitmapData(byte[] picBuff, Bitmap bitmap, int ignoreCol } } - private void CaptureAndQuit(Bitmap bitmap, int noFrames, string expectedFrameName) + private void CaptureAndQuit(Bitmap bitmap, string expectedFrameName) { - if (_capturedFrames.Count < noFrames) - { + if (saveEnabled) { if (Cropping != null) { bitmap = Crop(bitmap); } - bitmap.Save($"C:\\temp\\First100\\{expectedFrameName}_{_capturedFrames.Count}.bmp"); + bitmap.Save($"C:\\temp\\First100\\{expectedFrameName}_{_noCaptured}.bmp"); + _noCaptured++; } CopyRectToQueue(bitmap.Clone(new Rectangle(0, 0, bitmap.Width, bitmap.Height), bitmap.PixelFormat)); - } - else if (_capturedFrames.Count == noFrames && !hasSentQuit) - { - Quit(); - hasSentQuit = true; - } + } - protected void CaptureAndQuit(List screenBuffers, int noFrames, string expectedFrameName) + protected void CaptureAndQuit(List screenBuffers, string expectedFrameName) { screenBuffers.Where(b => b.PaletteBuffer != null).ToList().ForEach(b => UpdatePalettes(b.PaletteHash, b.PaletteBuffer)); @@ -319,7 +325,7 @@ protected void CaptureAndQuit(List screenBuffers, int noFrames, st Assert.IsTrue(screenBuffer.PictureBuffer == null || screenBuffer.PictureBuffer.Length == 0); } - CaptureAndQuit(_screenBuffers[screenBuffer.ScreenBufferHash], screenBuffer.IgnoreColour, screenBuffer.PaletteHash, screenBuffer.X, screenBuffer.Y, screenBuffer.W, screenBuffer.H, noFrames, expectedFrameName); + CaptureAndQuit(_screenBuffers[screenBuffer.ScreenBufferHash], screenBuffer.IgnoreColour, screenBuffer.PaletteHash, screenBuffer.X, screenBuffer.Y, screenBuffer.W, screenBuffer.H, expectedFrameName); } } @@ -396,7 +402,7 @@ public void CopyRectToQueue(Bitmap bitmap) } } - protected virtual async Task WaitForExpectedFrameAndQuit(string expectedFrameName, int noFrames, Task completeWhenQuit, int delay = 240000, int quitDelay = 240000) + protected virtual async Task WaitForExpectedFrameAndQuit(string expectedFrameName, Task completeWhenQuit, int delay = 240000, int quitDelay = 240000) { Task checkForFrame = Task.Run(async () => { @@ -410,7 +416,7 @@ protected virtual async Task WaitForExpectedFrameAndQuit(string expectedFrameNam if (!checkForFrame.IsCompleted) { - throw new Exception($"Did not display first {noFrames} frames in time"); + throw new Exception($"Did not display first frames in time"); } if (checkForFrame.IsFaulted) @@ -418,6 +424,8 @@ protected virtual async Task WaitForExpectedFrameAndQuit(string expectedFrameNam throw checkForFrame.Exception; } + Quit(); + await Task.WhenAny(completeWhenQuit, Task.Delay(quitDelay)); if (!completeWhenQuit.IsCompleted) diff --git a/ScummVmBrowser/DotNetScummTests/Properties/Resources.Designer.cs b/ScummVmBrowser/DotNetScummTests/Properties/Resources.Designer.cs index bd5fd8370c67..3ecb3d4ebf11 100644 --- a/ScummVmBrowser/DotNetScummTests/Properties/Resources.Designer.cs +++ b/ScummVmBrowser/DotNetScummTests/Properties/Resources.Designer.cs @@ -387,6 +387,15 @@ internal static string Kq3CanRestore { } } + /// + /// Looks up a localized string similar to XQAAAAStRwEAAD2IiWdR+qn2aKH22kcnaa+S2T9JEWJ/vNGWCQhjAGkepgrmmPA/gob+k1aFjP2UQlz6RZtSCi2zS02+mvaJT3cjJxRc0FkGJejby+36cCVR0sFlr+fW9ZKRaLYHKymXkPWadCIiAwVQ38yvt3Y13atEG+0htoGKqCmttB6QP7EjlysiSEuXIgODfoAqrT9VADfQWMrdD2R8cTdTOP5mRNVlWBzUqCb7Eoke77x//WUbDn9/IG6sAtyejqygDdbWQQplXdsUBUWR61F4h2mdl5h1zatElLfV2aCf9GTC5cqIzesJON37hVphCXvrvncBnGvG0QL86qaf4izdrB1/FHHGtBaNice0ZG3Crv8l2teuxit5lYoi/tBh7pZaUnYvBimZ/LKuC51y1NTwtDhZn5/7atzOB3j84/WVuFxItAMXo/rN70MsmbOATd0/lmWgIT5eFiBGE0vkpwB237KeMl0YxHXqOQenfv+WVgyOsAiZsSVMI947 [rest of string was truncated]";. + /// + internal static string Kq3JustOutsideOffice { + get { + return ResourceManager.GetString("Kq3JustOutsideOffice", resourceCulture); + } + } + /// /// Looks up a localized string similar to XQAAAARgdAIAAD2IiWdSDBzswLVpPG0N2Zj4vuZ/fzZYLMHDxu9sNeJLdQlqCOrOt2MKrNxfzVxSL9MUpFVobpzK15buVwqPccA83DCIeqpmui7fCFtZRrKzhzTUbRgDPo42tOcczEsvS+eWJNN4Grj1rFNmf6VIC/AmkEFp6HOFoauJGC4srP2N0Tqrw1YouxqZtw5yfgYDF+cs/hpD+NzUx/RKv0PrJlJ3KMe8UHNr/zcwQ5jsa1gjJpxYI1OYphaf+GsR/LuqWr+akKU4J7+/M5GuNwy/CdfX5n1/a/iWaTwrkFifAho49omZ/K/hQOXGUgd9BySlteHzEf8ySiXWOXh9JbssSlM5gu/Wx88bPWfSC/nQUKLBE4N/shLszgK6EjIYYwLnA7e5o1KC2wSW99On3pRnvQBTlFbwbpGr+Rw47dphH3kO1JEztTvjiRyWtxbenZ9IjxdrvUAP3JcjHIKdndX0cBt89XheEgr1/p+nAEOfJhhGvmJnKD/b [rest of string was truncated]";. /// @@ -396,6 +405,15 @@ internal static string Kq5CanStart { } } + /// + /// Looks up a localized string similar to XQAAAATb6wEAAD2IhmSy9HFo4DMadVuAiLM/E8PwZRnDgRnLiqcGRqv5+sfuZydFPw7zVKB0LCaccuLnHA0coqNtBospoihdNzazZFDxWhlA7mYWoQG3W/niJcUAMR+MqwUK775rGfvt5opTH6k08oej0eraL5BdxXpqhvX4QQNpoqAODzLr/zgNmAFU0tvjCh2H+SJsAmLOxH9SapiuYTi9YSEMLoBk0K+EzYaGyK4q80GK1F5rjqcsRPRESeSvG5/2418Fkry8XFw+1LRtVmFJHoyubjQ32kJCG67nGvEWoxjM2wEp09y5WFr39CZLfrYdRB9zFI+XRwcjXqO9JuHwR5IX7FzJ8bQX8Rhcl0SfJbfLVEfvbDrsfKw5wXOqDzeS6u/8hLQTxM4nHspsLMiS6fb/BdxiSM+4oMVxw7DcMK0mHcqQZAUq5MCpfjNJYGhSF4IA24nOC3Dh8rzRLR2/kBv4pYnNPl4voIztv9F5rGBgFhWYe5kCkurmxzmN [rest of string was truncated]";. + /// + internal static string SteelDoomed { + get { + return ResourceManager.GetString("SteelDoomed", resourceCulture); + } + } + /// /// Looks up a localized resource of type System.Drawing.Bitmap. /// diff --git a/ScummVmBrowser/DotNetScummTests/Properties/Resources.resx b/ScummVmBrowser/DotNetScummTests/Properties/Resources.resx index 6ab61bfade1b..a4cbf0142b8b 100644 --- a/ScummVmBrowser/DotNetScummTests/Properties/Resources.resx +++ b/ScummVmBrowser/DotNetScummTests/Properties/Resources.resx @@ -217,9 +217,15 @@ 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 + ..\Resources\Thumbnail.bmp;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a diff --git a/ScummVmBrowser/DotNetScummTests/WrapperFrameTests.cs b/ScummVmBrowser/DotNetScummTests/WrapperFrameTests.cs index 1b3c72939b04..6db9e0854c40 100644 --- a/ScummVmBrowser/DotNetScummTests/WrapperFrameTests.cs +++ b/ScummVmBrowser/DotNetScummTests/WrapperFrameTests.cs @@ -32,8 +32,9 @@ namespace DotNetScummTests [TestClass] public class WrapperFrameTests : FrameCaptureTests { - const int Timeout = 240000; - ScummGameService _wrapper; + const int ShortTimeout = 300000; + const int LongTimeout = 240000; + ScummGameService _wrapper; Task gameTask; string _saveData; ISaveDataEncoderAndDecompresser _saveDataEncoderAndDecompresser; @@ -48,8 +49,6 @@ public class WrapperFrameTests : FrameCaptureTests const int Kq4HotCursorOffset = 8; const string ThumbnailResourceName = "Thumbnail"; - - byte[] ExpectedAGISaveDataPrefix = new byte[] { 65, 71, 73, 58 }; //All AGI saves start with this [TestInitialize] @@ -58,9 +57,9 @@ public void Init() System.IO.File.Delete("scummvm.ini"); } - public void Setup(String gameFolderLocation, int noFrames, string expectedFrameName, AvailableGames game = AvailableGames.kq3, string saveDataResourceName = DefaultSavesDataResourceName, uint saveSlotToLoad = Constants.DoNotLoadSaveSlot) + public void Setup(String gameFolderLocation, string expectedFrameName, AvailableGames game = AvailableGames.kq3, string saveDataResourceName = DefaultSavesDataResourceName, uint saveSlotToLoad = Constants.DoNotLoadSaveSlot) { - Setup(gameFolderLocation, (List screenBuffers) => CaptureAndQuit(screenBuffers, noFrames, expectedFrameName), game, saveDataResourceName, saveSlotToLoad); + Setup(gameFolderLocation, (List screenBuffers) => CaptureAndQuit(screenBuffers, expectedFrameName), game, saveDataResourceName, saveSlotToLoad); } public void Setup(String gameFolderLocation, SendScreenBuffers copyRectToScreen, AvailableGames game = AvailableGames.kq3, string saveDataResourceName = DefaultSavesDataResourceName, uint saveSlotToLoad = Constants.DoNotLoadSaveSlot) @@ -86,110 +85,94 @@ public void Setup(String gameFolderLocation, SendScreenBuffers copyRectToScreen, _wrapper.StartSound(); } - [Timeout(Timeout)] + [Timeout(ShortTimeout)] [TestMethod] public async Task CanStart() { Cropping = null; const string expectedFrameName = "CanStart"; - const int noFrames = 100; - //DotNetScummTests.Properties.Resources.CanDoFirst100Frames__97_ - Setup(gameDirectory, (List screenBuffers) => CaptureAndQuit(screenBuffers, noFrames, expectedFrameName) ); - await CheckForExpectedFrame(expectedFrameName, noFrames); + + Setup(gameDirectory, (List screenBuffers) => CaptureAndQuit(screenBuffers, expectedFrameName) ); + await CheckForExpectedFrame(expectedFrameName); } - [Timeout(Timeout)] + [Timeout(LongTimeout)] //Also tests steel [TestMethod] public async Task CanRunGamesRequiringReentrantMutexes() { - const int MouseX = 172; - const int MouseY = 173; - - //Cropping = new Rectangle(0, 180, 10, 20); const string expectedFrameName = "CanRunGamesRequiringReentrantMutexes"; - const int noFrames = 2549; - Setup(gameDirectory, (List screenBuffers) => CaptureAndQuit(screenBuffers, noFrames, expectedFrameName), AvailableGames.steel); - await WaitForFrame(50); - _wrapper.EnqueueGameEvent(new SendControlCharacters(ControlKeys.Escape)); - await WaitForFrame(300); - _wrapper.EnqueueGameEvent(new SendMouseMove(MouseX,MouseY)); - await WaitForFrame(50); - EnqueueClick(MouseX, MouseY, MouseClick.Left); - - await CheckForExpectedFrame(expectedFrameName, noFrames); + Setup(gameDirectory, (List screenBuffers) => CaptureAndQuit(screenBuffers, expectedFrameName), AvailableGames.steel, SteelDoomed, 1); + await CheckForExpectedFrame(expectedFrameName); } - [Timeout(Timeout)] + [Timeout(ShortTimeout)] [TestMethod] public async Task CanMoveMousePaletteDisabled() { Point dimensions = GetDisplayDimensionsByGame(AvailableGames.kq4); Cropping = null; const string expectedFrameName = "CanMoveMousePaletteDisabled"; - const int noFrames = 25; - Setup(gameDirectory, (List screenBuffers) => CaptureAndQuit(screenBuffers, noFrames, expectedFrameName), AvailableGames.kq4); + Setup(gameDirectory, (List screenBuffers) => CaptureAndQuit(screenBuffers, expectedFrameName), AvailableGames.kq4); await WaitForFrame(10); //_wrapper.EnqueueGameEvent(new SendMouseMove(DisplayDefaultWidth + Kq4HotCursorOffset - 1, DisplayDefaultHeight + Kq4HotCursorOffset - 1)); _wrapper.EnqueueGameEvent(new SendMouseMove(dimensions.X + Kq4HotCursorOffset - 1, dimensions.Y + Kq4HotCursorOffset - 1)); await WaitAdditionalFrames(10); _wrapper.EnqueueGameEvent(new SendMouseMove(Kq4HotCursorOffset, Kq4HotCursorOffset)); - await CheckForExpectedFrame(expectedFrameName, noFrames); + await CheckForExpectedFrame(expectedFrameName); } - [Timeout(Timeout)] + [Timeout(ShortTimeout)] [TestMethod] public async Task DoesDisplayBlackFirstFrame() { Cropping = null; const string expectedFrameName = "DoesDisplayBlackFirstFrame"; - const int noFrames = 1; - //DotNetScummTests.Properties.Resources.CanDoFirst100Frames__97_ - Setup(gameDirectory, (List screenBuffers) => CaptureAndQuit(screenBuffers, noFrames, expectedFrameName)); - await CheckForExpectedFrame(expectedFrameName, noFrames); + + Setup(gameDirectory, (List screenBuffers) => CaptureAndQuit(screenBuffers, expectedFrameName)); + await CheckForExpectedFrame(expectedFrameName); } - [Timeout(Timeout)] + [Timeout(ShortTimeout)] [TestMethod] public async Task CanRunGameRequiringLockScreen() { Cropping = new Rectangle(100, 100, 20, 20); const string expectedFrameName = "CanRunGameRequiringLockScreen"; - const int noFrames = 530; - Setup(gameDirectory, noFrames, expectedFrameName, AvailableGames.kq4, KingsQuest4OnMountain, 1); - await CheckForExpectedFrame(expectedFrameName, noFrames); + + Setup(gameDirectory, expectedFrameName, AvailableGames.kq4, KingsQuest4OnMountain, 1); + await CheckForExpectedFrame(expectedFrameName); } - [Timeout(Timeout)] + [Timeout(ShortTimeout)] [TestMethod] public async Task CanStartKq5() { Cropping = null; const string expectedFrameName = "CanStartKq5"; - const int noFrames = 400; - Setup(gameDirectory, noFrames, expectedFrameName, AvailableGames.kq5); - await CheckForExpectedFrame(expectedFrameName, noFrames); + Setup(gameDirectory, expectedFrameName, AvailableGames.kq5); + await CheckForExpectedFrame(expectedFrameName); } //Also Tests Kq6 - [Timeout(Timeout)] + //[Timeout(Timeout)] [TestMethod] public async Task CanRunHiResGames() { Cropping = new Rectangle(360,180,30,30); //This is a deliberate placement we want to see the character portrait we need to check for Kq6 const string expectedFrameName = "CanRunHiResGames"; - const int noFrames = 90; - Setup(gameDirectory, noFrames, expectedFrameName, AvailableGames.kq6, KingsQuest6BeforeBeautySpeaks, 1); + + Setup(gameDirectory, expectedFrameName, AvailableGames.kq6, KingsQuest6BeforeBeautySpeaks, 1); await WaitForFrame(15); _wrapper.EnqueueGameEvent(new SendMouseMove(15, 200)); - await CheckForExpectedFrame(expectedFrameName, noFrames); + await CheckForExpectedFrame(expectedFrameName); } - [Timeout(Timeout)] + [Timeout(ShortTimeout)] [TestMethod] public async Task CanClickButtonRequiringLongMousePress() { @@ -200,8 +183,7 @@ public async Task CanClickButtonRequiringLongMousePress() Cropping = new Rectangle(100, 0, 20, 30); const string expectedFrameName = "CanClickButtonRequiringLongMousePress"; - const int noFrames = 220; - Setup(gameDirectory, (List screenBuffers) => CaptureAndQuit(screenBuffers, noFrames, expectedFrameName), AvailableGames.steel); + Setup(gameDirectory, (List screenBuffers) => CaptureAndQuit(screenBuffers, expectedFrameName), AvailableGames.steel); await WaitForFrame(50); _wrapper.EnqueueGameEvent(new SendControlCharacters(ControlKeys.Escape)); @@ -209,7 +191,7 @@ public async Task CanClickButtonRequiringLongMousePress() _wrapper.EnqueueGameEvent(new SendMouseMove(MouseX, MouseY)); _wrapper.EnqueueGameEvent(new SendControlCharacters(ControlKeys.F5)); - await WaitForFrame(199); + await WaitForFrame(190); _wrapper.EnqueueGameEvent(new SendMouseMove(MouseX, MouseY)); _wrapper.EnqueueGameEvent(new SendMouseClick(MouseClick.Left, () => new Point(MouseX, MouseY), MouseUpDown.MouseDown)); @@ -217,22 +199,21 @@ public async Task CanClickButtonRequiringLongMousePress() _wrapper.EnqueueGameEvent(new SendMouseClick(MouseClick.Left, () => new Point(MouseX, MouseY), MouseUpDown.MouseUp)); - await CheckForExpectedFrame(expectedFrameName, noFrames); + await CheckForExpectedFrame(expectedFrameName); } - [Timeout(Timeout)] + [Timeout(LongTimeout)] [TestMethod] public async Task CanRunGamesWithMusicTimer() { const string expectedFrameName = "CanRunGamesWithMusicTimer"; - const int noFrames = 1500; Cropping = new Rectangle(238, 20, 50, 20); - //Cropping = new Rectangle(238, 40, 50, 20); - Setup(gameDirectory, noFrames, expectedFrameName, AvailableGames.smi, Kq5CanStart, Constants.DoNotLoadSaveSlot); - await CheckForExpectedFrame(expectedFrameName, noFrames); + + Setup(gameDirectory, expectedFrameName, AvailableGames.smi, Kq5CanStart, Constants.DoNotLoadSaveSlot); + await CheckForExpectedFrame(expectedFrameName); } - [Timeout(Timeout)] + [Timeout(ShortTimeout)] [TestMethod] public async Task CanClickNonZeroHotSpot() { @@ -241,56 +222,50 @@ public async Task CanClickNonZeroHotSpot() Cropping = null; const string expectedFrameName = "CanClickNonZeroHotSpot"; - const int noFrames = 1200; - Setup(gameDirectory, (List screenBuffers) => CaptureAndQuit(screenBuffers, noFrames, expectedFrameName), AvailableGames.kq4); + Setup(gameDirectory, (List screenBuffers) => CaptureAndQuit(screenBuffers, expectedFrameName), AvailableGames.kq4); await WaitForFrame(18); _wrapper.EnqueueGameEvent(new SendString(KingsQuest4CopyProtectionWord)); _wrapper.EnqueueGameEvent(new SendString("\r")); - await WaitForFrame(795); + await WaitForFrame(419); _wrapper.EnqueueGameEvent(new SendMouseMove(MouseX, MouseY)); EnqueueClick(MouseX, MouseY, MouseClick.Left); - await CheckForExpectedFrame(expectedFrameName, noFrames); + await CheckForExpectedFrame(expectedFrameName); } - [Timeout(Timeout)] + [Timeout(ShortTimeout)] [TestMethod] public async Task CanStartWholeFrame() { Cropping = null; const string expectedFrameName = "CanStart"; - const int noFrames = 100; - //DotNetScummTests.Properties.Resources.CanDoFirst100Frames__97_ - Setup(gameDirectory, (List screenBuffers) => CaptureAndQuitWholeFrame(screenBuffers, noFrames, expectedFrameName)); + Setup(gameDirectory, (List screenBuffers) => CaptureAndQuitWholeFrame(screenBuffers, expectedFrameName)); await WaitForFrame(10); _wrapper.ScheduleRedrawWholeScreen(); - await CheckForExpectedFrame(expectedFrameName, noFrames); + await CheckForExpectedFrame(expectedFrameName); } - [Timeout(Timeout)] + [Timeout(ShortTimeout)] [TestMethod] public async Task CanSendEnter() { Cropping = new Rectangle(5, 0, 10, 10); const string expectedFrameName = "CanSendEnter"; - const int noFrames = 150; - //DotNetScummTests.Properties.Resources.CanDoFirst100Frames__97_ - Setup(gameDirectory, noFrames, expectedFrameName); + + Setup(gameDirectory, expectedFrameName); await WaitForFrame(10); _wrapper.EnqueueGameEvent(new SendString("\r")); - await CheckForExpectedFrame(expectedFrameName, noFrames); + await CheckForExpectedFrame(expectedFrameName); } - [Timeout(Timeout)] + [Timeout(ShortTimeout)] [TestMethod] public async Task CanSendEsc() { Cropping = new Rectangle(5, 0, 10, 30); const string expectedFrameName = "CanSendEsc"; - const int noFrames = 310; - //DotNetScummTests.Properties.Resources.CanDoFirst100Frames__97_ - Setup(gameDirectory, noFrames, expectedFrameName); + Setup(gameDirectory, expectedFrameName); await WaitForFrame(180); _wrapper.EnqueueGameEvent(new SendString("\r")); _wrapper.EnqueueGameEvent(new SendControlCharacters(ControlKeys.Escape)); @@ -298,17 +273,16 @@ public async Task CanSendEsc() await Task.Delay(1000); _wrapper.EnqueueGameEvent(new SendControlCharacters(ControlKeys.Escape)); - await CheckForExpectedFrame(expectedFrameName, noFrames); + await CheckForExpectedFrame(expectedFrameName); } - [Timeout(Timeout)] + [Timeout(ShortTimeout)] [TestMethod] public async Task CanSendF1_Help() { const string expectedFrameName = "CanSendF1"; - const int noFrames = 700; //DotNetScummTests.Properties.Resources.CanDoFirst100Frames__97_ - Setup(gameDirectory, noFrames, expectedFrameName); + Setup(gameDirectory, expectedFrameName); await WaitForFrame(180); _wrapper.EnqueueGameEvent(new SendString("\r")); _wrapper.EnqueueGameEvent(new SendControlCharacters(ControlKeys.F1)); @@ -316,126 +290,111 @@ public async Task CanSendF1_Help() await Task.Delay(1000); _wrapper.EnqueueGameEvent(new SendString("\r")); - await CheckForExpectedFrame(expectedFrameName, noFrames); + await CheckForExpectedFrame(expectedFrameName); } - [Timeout(Timeout)] + [Timeout(ShortTimeout)] [TestMethod] public async Task CanSendF2_Sound() { Cropping = AgiTitleOnly; const string expectedFrameName = "CanSendF2"; - const int noFrames = 500; - //DotNetScummTests.Properties.Resources.CanDoFirst100Frames__97_ - Setup(gameDirectory, noFrames, expectedFrameName); + Setup(gameDirectory, expectedFrameName); await WaitForFrame(180); _wrapper.EnqueueGameEvent(new SendMouseMove(100,100)); //Mouse is in the way _wrapper.EnqueueGameEvent(new SendString("\r")); _wrapper.EnqueueGameEvent(new SendControlCharacters(ControlKeys.F2)); - await CheckForExpectedFrame(expectedFrameName, noFrames); + await CheckForExpectedFrame(expectedFrameName); } //F3 Is Repeat Which Requires That ASCII be done first - [Timeout(Timeout)] + [Timeout(ShortTimeout)] [TestMethod] public async Task CanSendF4_Inventory() { Cropping = new Rectangle(100, 100, 10, 10); const string expectedFrameName = "CanSendF4"; - const int noFrames = 350; - //DotNetScummTests.Properties.Resources.CanDoFirst100Frames__97_ - Setup(gameDirectory, noFrames, expectedFrameName); + Setup(gameDirectory, expectedFrameName); await WaitForFrame(250); _wrapper.EnqueueGameEvent(new SendString("\r")); await WaitForFrame(300); _wrapper.EnqueueGameEvent(new SendControlCharacters(ControlKeys.F4)); - await CheckForExpectedFrame(expectedFrameName, noFrames); + await CheckForExpectedFrame(expectedFrameName); } - [Timeout(Timeout)] + [Timeout(ShortTimeout)] [TestMethod] public async Task CanSendString() { - Cropping = new Rectangle(86, 83, 100, 20); + Cropping = new Rectangle(86, 83, 100, 10); const string expectedFrameName = "CanSendString"; - const int noFrames = 120; - //DotNetScummTests.Properties.Resources.CanDoFirst100Frames__97_ - Setup(gameDirectory, noFrames, expectedFrameName); + Setup(gameDirectory, expectedFrameName); await WaitForFrame(10); _wrapper.EnqueueGameEvent(new SendString("\r")); await WaitForFrame(70); _wrapper.EnqueueGameEvent(new SendString("AbcdEFg")); _wrapper.EnqueueGameEvent(new SendString("\r")); - await WaitForFrame(115); - _wrapper.EnqueueGameEvent(new SendString("\r")); await Task.Delay(1000); _wrapper.EnqueueGameEvent(new SendString("\r")); - await CheckForExpectedFrame(expectedFrameName, noFrames); + await CheckForExpectedFrame(expectedFrameName); } - [Timeout(Timeout)] + [Timeout(ShortTimeout)] [TestMethod] public async Task CanSendDownArrow() { - Cropping = new Rectangle(52, 68, 140, 20); + Cropping = new Rectangle(52, 68, 100, 20); const string expectedFrameName = "CanSendDownArrow"; - const int noFrames = 122; //DotNetScummTests.Properties.Resources.CanDoFirst100Frames__97_ - Setup(gameDirectory, noFrames, expectedFrameName); + Setup(gameDirectory, expectedFrameName); await WaitForFrame(15); _wrapper.EnqueueGameEvent(new SendString("\r")); await WaitAdditionalFrames(10); _wrapper.EnqueueGameEvent(new SendControlCharacters(ControlKeys.ArrowDown)); - await CheckForExpectedFrame(expectedFrameName, noFrames); + await CheckForExpectedFrame(expectedFrameName); } - [Timeout(Timeout)] + [Timeout(ShortTimeout)] [TestMethod] public async Task CanSendUp() { - Cropping = new Rectangle(41, 82, 30, 30); const string expectedFrameName = "CanSendUp"; - const int noFrames = 263; - Setup(gameDirectory, noFrames, expectedFrameName); - await WaitForFrame(15); - _wrapper.EnqueueGameEvent(new SendString("\r")); - await WaitForFrame(72); + Setup(gameDirectory, expectedFrameName, AvailableGames.kq3, Kq3JustOutsideOffice, 1); + await WaitAdditionalFrames(10); _wrapper.EnqueueGameEvent(new SendControlCharacters(ControlKeys.ArrowUp)); - await CheckForExpectedFrame(expectedFrameName, noFrames); + await CheckForExpectedFrame(expectedFrameName); } - [Timeout(Timeout)] + [Timeout(ShortTimeout)] [TestMethod] public async Task CanSendLeft() { Cropping = new Rectangle(0,0, 30, 10); const string expectedFrameName = "CanSendLeft"; - const int noFrames = 140; - Setup(gameDirectory, noFrames, expectedFrameName); + Setup(gameDirectory, expectedFrameName); await WaitForFrame(30); _wrapper.EnqueueGameEvent(new SendString("\r")); await WaitAdditionalFrames(10); _wrapper.EnqueueGameEvent(new SendControlCharacters(ControlKeys.Escape)); - await WaitAdditionalFrames(10); + await WaitAdditionalFrames(2); _wrapper.EnqueueGameEvent(new SendControlCharacters(ControlKeys.ArrowLeft)); await Task.Delay(1000); - await CheckForExpectedFrame(expectedFrameName, noFrames); + await CheckForExpectedFrame(expectedFrameName); } - [Timeout(Timeout)] + [Timeout(ShortTimeout)] [TestMethod] public async Task CanSendRight() { Cropping = new Rectangle(55, 0, 155, 30); const string expectedFrameName = "CanSendRight"; - const int noFrames = 210; //DotNetScummTests.Properties.Resources.CanDoFirst100Frames__97_ - Setup(gameDirectory, noFrames, expectedFrameName); + Setup(gameDirectory, expectedFrameName); _wrapper.EnqueueGameEvent(new SendMouseMove(100,100)); //Move mouse out of the way await WaitForFrame(20); _wrapper.EnqueueGameEvent(new SendString("\r")); @@ -447,18 +406,17 @@ public async Task CanSendRight() await Task.Delay(3000); _wrapper.EnqueueGameEvent(new SendControlCharacters(ControlKeys.Escape)); - await CheckForExpectedFrame(expectedFrameName, noFrames); + await CheckForExpectedFrame(expectedFrameName); } - [Timeout(Timeout)] + [Timeout(ShortTimeout)] [TestMethod] public async Task CanSendBackspace() { Cropping = new Rectangle(86, 83, 138, 18); const string expectedFrameName = "CanSendBackspace"; - const int noFrames = 300; - Setup(gameDirectory, noFrames, expectedFrameName); + Setup(gameDirectory, expectedFrameName); await WaitForFrame(180); _wrapper.EnqueueGameEvent(new SendString("\r")); @@ -473,21 +431,20 @@ public async Task CanSendBackspace() await Task.Delay(1000); _wrapper.EnqueueGameEvent(new SendString("\r")); - await CheckForExpectedFrame(expectedFrameName, noFrames); + await CheckForExpectedFrame(expectedFrameName); } - [Timeout(Timeout)] + [Timeout(ShortTimeout)] [TestMethod] public async Task CanSendBackspaceViaControlKey() { Cropping = new Rectangle(86, 83, 138, 18); const string expectedFrameName = "CanSendBackspace"; - const int noFrames = 300; - //DotNetScummTests.Properties.Resources.CanDoFirst100Frames__97_ - Setup(gameDirectory, noFrames, expectedFrameName); + + Setup(gameDirectory, expectedFrameName); await WaitForFrame(10); _wrapper.EnqueueGameEvent(new SendString("\r")); - await WaitAdditionalFrames(10); + await WaitAdditionalFrames(5); _wrapper.EnqueueGameEvent(new SendString("AbcdEFg")); await WaitAdditionalFrames(10); _wrapper.EnqueueGameEvent(new SendControlCharacters(ControlKeys.Backspace)); @@ -497,17 +454,16 @@ public async Task CanSendBackspaceViaControlKey() await Task.Delay(1000); _wrapper.EnqueueGameEvent(new SendString("\r")); - await CheckForExpectedFrame(expectedFrameName, noFrames); + await CheckForExpectedFrame(expectedFrameName); } - [Timeout(Timeout)] + [Timeout(ShortTimeout)] [TestMethod] public async Task CanSendTab() { const string expectedFrameName = "CanSendTab"; - const int noFrames = 500; - Setup(gameDirectory, noFrames, expectedFrameName); + Setup(gameDirectory, expectedFrameName); await WaitForFrame(180); _wrapper.EnqueueGameEvent(new SendString("\r")); await WaitAdditionalFrames(50); @@ -520,140 +476,133 @@ public async Task CanSendTab() await Task.Delay(1000); _wrapper.EnqueueGameEvent(new SendString("\r")); - await CheckForExpectedFrame(expectedFrameName, noFrames); + await CheckForExpectedFrame(expectedFrameName); } - [Timeout(Timeout)] + [Timeout(ShortTimeout)] [TestMethod] public async Task CanMoveMouse() { Cropping = new Rectangle(28, 0, 51, 35); const string expectedFrameName = "CanMoveMouse"; - const int noFrames = 100; - - Setup(gameDirectory, (List screenBuffers) => CaptureAndQuit(screenBuffers, noFrames, expectedFrameName)); + + Setup(gameDirectory, (List screenBuffers) => CaptureAndQuit(screenBuffers, expectedFrameName)); await WaitForFrame(10); _wrapper.EnqueueGameEvent(new SendMouseMove(44,15)); - await CheckForExpectedFrame(expectedFrameName, noFrames); + await CheckForExpectedFrame(expectedFrameName); } - [Timeout(Timeout)] + [Timeout(ShortTimeout)] [TestMethod] public async Task CanMoveMouseMultipleTimes() { Cropping = new Rectangle(93, 162, 15, 15); const string expectedFrameName = "CanMoveMouseMultipleTimes"; - const int noFrames = 100; - Setup(gameDirectory, (List screenBuffers) => CaptureAndQuit(screenBuffers, noFrames, expectedFrameName)); + Setup(gameDirectory, (List screenBuffers) => CaptureAndQuit(screenBuffers, expectedFrameName)); await WaitForFrame(10); _wrapper.EnqueueGameEvent(new SendMouseMove(44, 15)); _wrapper.EnqueueGameEvent(new SendMouseMove(12, 35)); _wrapper.EnqueueGameEvent(new SendMouseMove(72, 55)); _wrapper.EnqueueGameEvent(new SendMouseMove(100, 160)); - await CheckForExpectedFrame(expectedFrameName, noFrames); + await CheckForExpectedFrame(expectedFrameName); } - [Timeout(Timeout)] + [Timeout(ShortTimeout)] [TestMethod] public async Task CanMoveMouseFarLeft() { Cropping = new Rectangle(0,0,30,55); const string expectedFrameName = "CanMoveMouseFarLeft"; - const int noFrames = 100; - Setup(gameDirectory, (List screenBuffers) => CaptureAndQuit(screenBuffers, noFrames, expectedFrameName)); + Setup(gameDirectory, (List screenBuffers) => CaptureAndQuit(screenBuffers, expectedFrameName)); await WaitForFrame(10); _wrapper.EnqueueGameEvent(new SendMouseMove(0, 15)); - await CheckForExpectedFrame(expectedFrameName, noFrames); + await CheckForExpectedFrame(expectedFrameName); } - [Timeout(Timeout)] + [Timeout(ShortTimeout)] [TestMethod] public async Task CanMoveMouseFarRight() { Cropping = new Rectangle(300,15,20,55); const string expectedFrameName = "CanMoveMouseFarRight"; - const int noFrames = 100; - Setup(gameDirectory, (List screenBuffers) => CaptureAndQuit(screenBuffers, noFrames, expectedFrameName)); + Setup(gameDirectory, (List screenBuffers) => CaptureAndQuit(screenBuffers, expectedFrameName)); await WaitForFrame(10); _wrapper.EnqueueGameEvent(new SendMouseMove(320, 15)); - await CheckForExpectedFrame(expectedFrameName, noFrames); + await CheckForExpectedFrame(expectedFrameName); } - [Timeout(Timeout)] + [Timeout(ShortTimeout)] [TestMethod] public async Task CanMoveMouseFarTop() { Cropping = new Rectangle(0, 0, 55, 55); const string expectedFrameName = "CanMoveMouseFarTop"; - const int noFrames = 100; - Setup(gameDirectory, (List screenBuffers) => CaptureAndQuit(screenBuffers, noFrames, expectedFrameName)); + Setup(gameDirectory, (List screenBuffers) => CaptureAndQuit(screenBuffers, expectedFrameName)); await WaitForFrame(10); _wrapper.EnqueueGameEvent(new SendMouseMove(15, 0)); - await CheckForExpectedFrame(expectedFrameName, noFrames); + await CheckForExpectedFrame(expectedFrameName); } - [Timeout(Timeout)] + [Timeout(ShortTimeout)] [TestMethod] public async Task CanMoveMouseFarBottom() { Cropping = new Rectangle(0, 180, 55, 20); const string expectedFrameName = "CanMoveMouseFarBottom"; - const int noFrames = 100; - Setup(gameDirectory, (List screenBuffers) => CaptureAndQuit(screenBuffers, noFrames, expectedFrameName)); + Setup(gameDirectory, (List screenBuffers) => CaptureAndQuit(screenBuffers, expectedFrameName)); await WaitForFrame(10); _wrapper.EnqueueGameEvent(new SendMouseMove(15, 200)); - await CheckForExpectedFrame(expectedFrameName, noFrames); + await CheckForExpectedFrame(expectedFrameName); } - [Timeout(Timeout)] + [Timeout(ShortTimeout)] [TestMethod] public async Task CursorCropsFarRight() { Cropping = new Rectangle(300, 15, 20, 55); const string expectedFrameName = "CursorCropsFarRight"; - const int noFrames = 100; - Setup(gameDirectory, (List screenBuffers) => CaptureAndQuit(screenBuffers, noFrames, expectedFrameName)); + Setup(gameDirectory, (List screenBuffers) => CaptureAndQuit(screenBuffers, expectedFrameName)); await WaitForFrame(10); _wrapper.EnqueueGameEvent(new SendMouseMove(315, 15)); - await CheckForExpectedFrame(expectedFrameName, noFrames); + await CheckForExpectedFrame(expectedFrameName); } - [Timeout(Timeout)] + [Timeout(ShortTimeout)] [TestMethod] public async Task CursorCropsFarBottom() { Cropping = new Rectangle(0, 180, 55, 20); const string expectedFrameName = "CanMoveMouseFarBottom"; - const int noFrames = 100; - Setup(gameDirectory, (List screenBuffers) => CaptureAndQuit(screenBuffers, noFrames, expectedFrameName)); + Setup(gameDirectory, (List screenBuffers) => CaptureAndQuit(screenBuffers, expectedFrameName)); await WaitForFrame(10); _wrapper.EnqueueGameEvent(new SendMouseMove(15, 195)); - await CheckForExpectedFrame(expectedFrameName, noFrames); + await CheckForExpectedFrame(expectedFrameName); } - [Timeout(Timeout)] + [Timeout(ShortTimeout)] [TestMethod] public async Task CanClick() { const string expectedFrameName = "CanSendEnter"; - const int noFrames = 250; - Setup(gameDirectory, noFrames, expectedFrameName); + Cropping = new Rectangle(100, 0, 20, 30); + + Setup(gameDirectory, expectedFrameName); await WaitForFrame(180); int mouseX = _wrapper.GetCurrentMousePosition().X; @@ -661,16 +610,16 @@ public async Task CanClick() EnqueueClick(mouseX, mouseY, MouseClick.Left); - await CheckForExpectedFrame(expectedFrameName, noFrames); + await CheckForExpectedFrame(expectedFrameName); } - [Timeout(Timeout)] + [Timeout(ShortTimeout)] [TestMethod] public async Task CanSave() { Cropping = new Rectangle(0, 0, 160, 80); - const int noFrames = 1000; - Setup(gameDirectory, noFrames, "_");//Not checking against the capture frames + + Setup(gameDirectory, "_");//Not checking against the capture frames await WaitForFrame(10); @@ -705,24 +654,23 @@ public async Task CanSave() await QuitAsync(); } - [Timeout(Timeout)] + [Timeout(ShortTimeout)] [TestMethod] public async Task CanRestore() { Cropping = new Rectangle(0, 20, 100, 30); - const int noFrames = 1000; const string expectedFrameName = "CanRestore"; - Setup(gameDirectory, noFrames, expectedFrameName); + Setup(gameDirectory, expectedFrameName); await WaitForFrame(20); _wrapper.EnqueueGameEvent(new SendString("\r")); - await WaitAdditionalFrames(160); + await WaitAdditionalFrames(40); await Restore(); - await CheckForExpectedFrame(expectedFrameName, noFrames); + await CheckForExpectedFrame(expectedFrameName); } private async Task Save() @@ -753,9 +701,9 @@ private async Task Restore() _wrapper.EnqueueGameEvent(new SendString("\r")); } - private async Task CheckForExpectedFrame(string expectedFrameName, int noFrames, int delay = 240000) + private async Task CheckForExpectedFrame(string expectedFrameName, int delay = 240000) { - await WaitForExpectedFrameAndQuit(expectedFrameName, noFrames, gameTask, delay); + await WaitForExpectedFrameAndQuit(expectedFrameName, gameTask, delay); } private void RunGame(AvailableGames game = AvailableGames.kq3, string saveDataResourceName = DefaultSavesDataResourceName, uint saveSlotToLoad = Constants.DoNotLoadSaveSlot) diff --git a/ScummVmBrowser/JsonResxConfigureStore/Resources/Dev/CliScummSettings.json b/ScummVmBrowser/JsonResxConfigureStore/Resources/Dev/CliScummSettings.json index d490bd4ea6de..61581c5fe2ac 100644 --- a/ScummVmBrowser/JsonResxConfigureStore/Resources/Dev/CliScummSettings.json +++ b/ScummVmBrowser/JsonResxConfigureStore/Resources/Dev/CliScummSettings.json @@ -1,3 +1,4 @@ { - "ScreenBufferCacheSize": 500 + "ScreenBufferCacheSize": 500, + "NoBuffersBeforeWholeScreen": 20 } diff --git a/ScummVmBrowser/JsonResxConfigureStore/Resources/Dev/SoundSettings.json b/ScummVmBrowser/JsonResxConfigureStore/Resources/Dev/SoundSettings.json index 37e9d6b4437e..5961c126a227 100644 --- a/ScummVmBrowser/JsonResxConfigureStore/Resources/Dev/SoundSettings.json +++ b/ScummVmBrowser/JsonResxConfigureStore/Resources/Dev/SoundSettings.json @@ -7,3 +7,4 @@ "ClientFeedSize": 3, "ServerFeedSize": 5 } + diff --git a/ScummVmBrowser/JsonResxConfigureStore/Resources/Prod/CliScummSettings.json b/ScummVmBrowser/JsonResxConfigureStore/Resources/Prod/CliScummSettings.json index d490bd4ea6de..61581c5fe2ac 100644 --- a/ScummVmBrowser/JsonResxConfigureStore/Resources/Prod/CliScummSettings.json +++ b/ScummVmBrowser/JsonResxConfigureStore/Resources/Prod/CliScummSettings.json @@ -1,3 +1,4 @@ { - "ScreenBufferCacheSize": 500 + "ScreenBufferCacheSize": 500, + "NoBuffersBeforeWholeScreen": 20 } diff --git a/ScummVmBrowser/JsonResxConfigureStore/Resources/Prod/ScummHubSettings.json b/ScummVmBrowser/JsonResxConfigureStore/Resources/Prod/ScummHubSettings.json index 4a2e46734efd..40d78f772a2e 100644 --- a/ScummVmBrowser/JsonResxConfigureStore/Resources/Prod/ScummHubSettings.json +++ b/ScummVmBrowser/JsonResxConfigureStore/Resources/Prod/ScummHubSettings.json @@ -1,4 +1,4 @@ { "KillProcessOnQuitTimeoutMs": 5000, - "BufferAndProcessSleepTime": 15 + "BufferAndProcessSleepTime": 70 } diff --git a/ScummVmBrowser/JsonResxConfigureStore/Resources/Prod/SoundSettings.json b/ScummVmBrowser/JsonResxConfigureStore/Resources/Prod/SoundSettings.json index 428de8b25ece..83cf8d6ef93b 100644 --- a/ScummVmBrowser/JsonResxConfigureStore/Resources/Prod/SoundSettings.json +++ b/ScummVmBrowser/JsonResxConfigureStore/Resources/Prod/SoundSettings.json @@ -1,9 +1,9 @@ { - "SampleSize": 2152, + "SampleSize": 2076, "SampleRate": 44000, "SoundPollingFrequencyMs": 10, "MaxQueuedToStopAudio": 10, - "RestAudioTime": 20, - "ClientFeedSize": 3, + "RestAudioTime": 15, + "ClientFeedSize": 2, "ServerFeedSize": 5 } diff --git a/ScummVmBrowser/ManagedCommon/Enums/Settings/CliScummSettings.cs b/ScummVmBrowser/ManagedCommon/Enums/Settings/CliScummSettings.cs index 23dcb8eb750b..cf1e3078d56c 100644 --- a/ScummVmBrowser/ManagedCommon/Enums/Settings/CliScummSettings.cs +++ b/ScummVmBrowser/ManagedCommon/Enums/Settings/CliScummSettings.cs @@ -3,6 +3,7 @@ namespace ScummWeb.Helpers.ManagedCommon.Enums.Settings { public enum CliScummSettings { - ScreenBufferCacheSize + ScreenBufferCacheSize, + NoBuffersBeforeWholeScreen } } diff --git a/ScummVmBrowser/NativeScummWrTests/MouseTests.cpp b/ScummVmBrowser/NativeScummWrTests/MouseTests.cpp index 02df29cb937e..acd8c734a23f 100644 --- a/ScummVmBrowser/NativeScummWrTests/MouseTests.cpp +++ b/ScummVmBrowser/NativeScummWrTests/MouseTests.cpp @@ -41,7 +41,7 @@ namespace TestCustomScummVMSubclasses TestNativeScummWrapperGraphics(f_SendScreenBuffers copyRect, NativeScummWrapperPaletteManager* _paletteManager) : NativeScummWrapperGraphics(copyRect, _paletteManager, _nativeScummWrapperOptions) { _nativeScummWrapperOptions = NativeScummWrapperOptions(); - _nativeScummWrapperOptions.ScreenBufferCacheSize = 500; + _nativeScummWrapperOptions.screenBufferCacheSize = 500; } void triggerUpdateScreen() { diff --git a/ScummVmBrowser/NativeScummWrTests/Releasex86/NativeScummWrTests/NativeScummWrTests.iobj b/ScummVmBrowser/NativeScummWrTests/Releasex86/NativeScummWrTests/NativeScummWrTests.iobj index 295abccf9584..10ec0fbcb0c7 100644 Binary files a/ScummVmBrowser/NativeScummWrTests/Releasex86/NativeScummWrTests/NativeScummWrTests.iobj and b/ScummVmBrowser/NativeScummWrTests/Releasex86/NativeScummWrTests/NativeScummWrTests.iobj differ diff --git a/ScummVmBrowser/NativeScummWrTests/ScreenBufferTests.cpp b/ScummVmBrowser/NativeScummWrTests/ScreenBufferTests.cpp index fa0082e4eb47..2eb3144bf151 100644 --- a/ScummVmBrowser/NativeScummWrTests/ScreenBufferTests.cpp +++ b/ScummVmBrowser/NativeScummWrTests/ScreenBufferTests.cpp @@ -10,7 +10,7 @@ TEST(AddingMoreThanScreenBufferCacheSizeCausesOldestEntryToBeRemoved) { const int SCREEN_CACHE_SIZE = 100; NativeScummWrapperOptions options; - options.ScreenBufferCacheSize = SCREEN_CACHE_SIZE; + options.screenBufferCacheSize = SCREEN_CACHE_SIZE; ScreenCache screenBuffer = ScreenCache(options); const int LENGTH = 5; @@ -40,7 +40,7 @@ TEST(AddingMoreThanScreenBufferCacheSizeCausesOldestEntryToBeRemoved) { TEST(CanAddHashToScreenCache) { NativeScummWrapperOptions options; - options.ScreenBufferCacheSize = 100; + options.screenBufferCacheSize = 100; ScreenCache screenBuffer = ScreenCache(options); diff --git a/ScummVmBrowser/SoundConverterTests/Releasex86/ChunkedSoundManagerTests/ChunkedSoundManagerTests.iobj b/ScummVmBrowser/SoundConverterTests/Releasex86/ChunkedSoundManagerTests/ChunkedSoundManagerTests.iobj index 768a8477ecc6..46e18ea48021 100644 Binary files a/ScummVmBrowser/SoundConverterTests/Releasex86/ChunkedSoundManagerTests/ChunkedSoundManagerTests.iobj and b/ScummVmBrowser/SoundConverterTests/Releasex86/ChunkedSoundManagerTests/ChunkedSoundManagerTests.iobj differ diff --git a/ScummVmBrowser/nativeScummWrapper/PaletteManager.h b/ScummVmBrowser/nativeScummWrapper/PaletteManager.h index dfb9a5d31d90..8b416348ae6c 100644 --- a/ScummVmBrowser/nativeScummWrapper/PaletteManager.h +++ b/ScummVmBrowser/nativeScummWrapper/PaletteManager.h @@ -6,7 +6,8 @@ namespace ScummWeb { namespace ScummService { namespace ScummWrapper { -class NativeScummWrapperPaletteManager { +class NativeScummWrapperPaletteManager +{ ScummWeb::ScummService::ScummWrapper::PalletteColor *_picturePalette; ScummWeb::ScummService::ScummWrapper::PalletteColor *_cursorPalette; byte *_pictureColor; diff --git a/ScummVmBrowser/nativeScummWrapper/common.h b/ScummVmBrowser/nativeScummWrapper/common.h index 7ceb285d80fe..c15771d36063 100644 --- a/ScummVmBrowser/nativeScummWrapper/common.h +++ b/ScummVmBrowser/nativeScummWrapper/common.h @@ -1,9 +1,8 @@ #pragma once +#include "scummVm.h" #include #include -#include "scummVm.h" - #define THUMBNAIL_DEFAULT_WIDTH 160 #define THUMBNAIL_DEFAULT_HEIGHT 100 @@ -41,30 +40,43 @@ struct ScreenBuffer { std::string screenBufferHash; }; +struct MinMaxDimensions { + int lowX; + int lowY; + int highW; + int highH; - inline std::string PadPaletteValue(uint value) { - std::string valueAsString = std::to_string(value); + MinMaxDimensions() { + lowX = 0; + lowY = 0; + highW = 0; + highH = 0; + } +}; - while (valueAsString.length() < 3) { - valueAsString = "0" + valueAsString; - } +inline std::string PadPaletteValue(uint value) { + std::string valueAsString = std::to_string(value); - return valueAsString; + while (valueAsString.length() < 3) { + valueAsString = "0" + valueAsString; } - inline std::string GetPaletteAsString(PalletteColor color) { - return ScummService::ScummWrapper::PadPaletteValue(color.r) + ScummService::ScummWrapper::PadPaletteValue(color.g) + ScummService::ScummWrapper::PadPaletteValue(color.b) + ScummService::ScummWrapper::PadPaletteValue(color.a); - } + return valueAsString; +} - inline std::string GetPalettesAsString(PalletteColor *pallette, int length) { - std::string toHash = ""; +inline std::string GetPaletteAsString(PalletteColor color) { + return ScummService::ScummWrapper::PadPaletteValue(color.r) + ScummService::ScummWrapper::PadPaletteValue(color.g) + ScummService::ScummWrapper::PadPaletteValue(color.b) + ScummService::ScummWrapper::PadPaletteValue(color.a); +} - for (int i = 0; i < length; i++) { - toHash = toHash + GetPaletteAsString(pallette[i]); - } +inline std::string GetPalettesAsString(PalletteColor *pallette, int length) { + std::string toHash = ""; - return toHash; + for (int i = 0; i < length; i++) { + toHash = toHash + GetPaletteAsString(pallette[i]); } + + return toHash; +} } // namespace ScummWrapper } // namespace ScummService } // namespace ScummWeb diff --git a/ScummVmBrowser/nativeScummWrapper/nativeScummWrapperGraphics.cpp b/ScummVmBrowser/nativeScummWrapper/nativeScummWrapperGraphics.cpp index d219137fe105..6429b8dfb9a4 100644 --- a/ScummVmBrowser/nativeScummWrapper/nativeScummWrapperGraphics.cpp +++ b/ScummVmBrowser/nativeScummWrapper/nativeScummWrapperGraphics.cpp @@ -14,6 +14,7 @@ ScummWeb::ScummService::ScummWrapper::NativeScummWrapperGraphics::NativeScummWra _isScreenMemoryInited = false; _width = 0; _height = 0; + _nativeScummWrapperOptions = nativeScummWrapperOptions; } ScummWeb::ScummService::ScummWrapper::NativeScummWrapperGraphics::~NativeScummWrapperGraphics() { @@ -25,15 +26,14 @@ void ScummWeb::ScummService::ScummWrapper::NativeScummWrapperGraphics::copyRectT WaitForSingleObject(_wholeScreenMutex, INFINITE); if (!_screenInited) { _screenInited = true; - int _; - byte *wholeScreenBuffer = GetWholeScreenBufferRaw(_, _, _); + + byte *wholeScreenBuffer = GetWholeScreenBufferRaw(); ScummWeb::ScummService::ScummWrapper::ScreenBuffer initScreen = GetScreenBuffer(wholeScreenBuffer, _width, 0, 0, _width, _height, _paletteManager->getCurrentCursorPaletteHash(), false); _drawingBuffers.push_back(initScreen); } - bool differenceDetected; byte *pictureBuffer = ScreenUpdated(buf, pitch, x, y, w, h, false, differenceDetected); @@ -118,7 +118,6 @@ void ScummWeb::ScummService::ScummWrapper::NativeScummWrapperGraphics::initSize( _wholeScreenBufferNoMouse = new byte[GetWholeScreenBufferLength()]; _isScreenMemoryInited = true; InitScreen(); - } ReleaseSemaphore(_wholeScreenMutex, 1, NULL); } @@ -146,7 +145,7 @@ void ScummWeb::ScummService::ScummWrapper::NativeScummWrapperGraphics::setPalett uint32 paletteHash = _paletteManager->createNewPaletteBasedOnPicturePalette(colors, start, num); _paletteManager->setCurrentPaletteHash(paletteHash); - ScheduleRedrawWholeScreen(); + ScheduleRedrawWholeScreen(false); } void ScummWeb::ScummService::ScummWrapper::NativeScummWrapperGraphics::grabPalette(byte *colors, uint start, uint num) const { @@ -171,15 +170,68 @@ void ScummWeb::ScummService::ScummWrapper::NativeScummWrapperGraphics::updateScr WaitForSingleObject(_wholeScreenMutex, INFINITE); if (!_drawingBuffers.empty()) { + if (_drawingBuffers.size() >= _nativeScummWrapperOptions.noBuffersBeforeWholeScreen) { + MinMaxDimensions minMaxDimensions = GetScreenBuffersMinMaxDimensions(); + + ClearScreenBufferMemory(); + ScheduleRedrawWholeScreenNoLock(false, minMaxDimensions.lowX, minMaxDimensions.lowY, (minMaxDimensions.highW + 1) - (minMaxDimensions.lowX + 1), (minMaxDimensions.highH + 1) - (minMaxDimensions.lowY + 1)); + } + UpdateScreenBuffersAccordingToCache(); NativeScummWrapperGraphics::_copyRect(&_drawingBuffers[0], _drawingBuffers.size()); + ClearScreenBufferMemory(); + } - for (int i = 0; i < _drawingBuffers.size(); i++) { + ReleaseSemaphore(_wholeScreenMutex, 1, NULL); +} + +void ScummWeb::ScummService::ScummWrapper::NativeScummWrapperGraphics::ClearScreenBufferMemory() { + for (int i = 0; i < _drawingBuffers.size(); i++) { + delete[] _drawingBuffers.at(i).buffer; + } + _drawingBuffers.clear(); +} + +void ScummWeb::ScummService::ScummWrapper::NativeScummWrapperGraphics::UpdateScreenBuffersScreenCachesAccordingToCache() { + for (int i = 0; i < _drawingBuffers.size(); i++) { + ScreenCacheAddResult screenCacheAddResult = _screenCache.AddScreenToCache(_drawingBuffers[i].buffer, _drawingBuffers[i].length); + + if (!screenCacheAddResult.firstTimeAdded) { delete[] _drawingBuffers.at(i).buffer; } - _drawingBuffers.clear(); + + _drawingBuffers[i].buffer = screenCacheAddResult.firstTimeAdded ? _drawingBuffers[i].buffer : nullptr; + _drawingBuffers[i].length = screenCacheAddResult.firstTimeAdded ? _drawingBuffers[i].length : 0; + _drawingBuffers[i].screenBufferHash = screenCacheAddResult.hash; } +} - ReleaseSemaphore(_wholeScreenMutex, 1, NULL); +void ScummWeb::ScummService::ScummWrapper::NativeScummWrapperGraphics::UpdateScreenBufferPallettesAccordingToCache() { + for (int i = 0; i < _drawingBuffers.size(); i++) { + if (_paletteManager->haveSeenPalette(_drawingBuffers[i].paletteHash)) { + _drawingBuffers[i].paletteBuffer = nullptr; + _drawingBuffers[i].paletteBufferLength = 0; + } else { + _paletteManager->registerSeenPalette(_drawingBuffers[i].paletteHash); + } + } +} + +byte *ScummWeb::ScummService::ScummWrapper::NativeScummWrapperGraphics::reduceBuffer(const void *buf, int x, int y, int w, int h, int &size) { + byte *result = new byte[w * h]; + byte *bufferAsByte = (byte *)buf + y * _width + x; + + for (int i = 0, resultCounter = 0; i < h; i = i++) { + for (int j = 0; j < w; j++, resultCounter++) { + result[resultCounter] = bufferAsByte[i * _width + j]; + } + } + + return result; +} + +void ScummWeb::ScummService::ScummWrapper::NativeScummWrapperGraphics::UpdateScreenBuffersAccordingToCache() { + UpdateScreenBufferPallettesAccordingToCache(); + UpdateScreenBuffersScreenCachesAccordingToCache(); } void ScummWeb::ScummService::ScummWrapper::NativeScummWrapperGraphics::setShakePos(int shakeXOffset, int shakeYOffset) { @@ -345,34 +397,53 @@ byte *ScummWeb::ScummService::ScummWrapper::NativeScummWrapperGraphics::GetBlott return pictureBuffer; } -void ScummWeb::ScummService::ScummWrapper::NativeScummWrapperGraphics::ScheduleRedrawWholeScreen(bool emptyScreenBufferCache) { +void ScummWeb::ScummService::ScummWrapper::NativeScummWrapperGraphics::ScheduleRedrawWholeScreen(bool clearCache) { WaitForSingleObject(_wholeScreenMutex, INFINITE); + ScheduleRedrawWholeScreenNoLock(clearCache); + ReleaseSemaphore(_wholeScreenMutex, 1, NULL); +} + +void ScummWeb::ScummService::ScummWrapper::NativeScummWrapperGraphics::ScheduleRedrawWholeScreenNoLock(bool clearCache, int x, int y, int w, int h) { if (_isScreenMemoryInited) { - if (emptyScreenBufferCache) { + + if (clearCache) { _drawingBuffers.clear(); ClearScreenBufferCache(); _paletteManager->clearPalettesSeen(); } - byte *cpyWholeScreenBuffer = new byte[GetWholeScreenBufferLength()]; + if (w < -1) { + throw std::string("Out of bounds width in scheduleRedrawWholeScreen"); + } + + if (h < -1) { + throw std::string("Out of bounds height in scheduleRedrawWholeScreen"); + } + + if (w == -1) { + w = _width; + } + + if (h == -1) { + h = _height; + } + + byte *cpyWholeScreenBuffer = GetWholeScreenBufferRaw(); - memcpy(cpyWholeScreenBuffer, _wholeScreenBufferNoMouse, GetWholeScreenBufferLength()); + int reducedBufferSize; + byte *reducedBuffer = reduceBuffer(cpyWholeScreenBuffer, x, y, w, h, reducedBufferSize); + delete cpyWholeScreenBuffer; - _drawingBuffers.push_back(GetScreenBuffer(cpyWholeScreenBuffer, _width, 0, 0, _width, _height, _paletteManager->getCurrentPaletteHash(), false)); + _drawingBuffers.push_back(GetScreenBuffer(reducedBuffer, _width, x, y, w, h, _paletteManager->getCurrentPaletteHash(), false)); _drawingBuffers.push_back(GetMouseScreenBuffer()); } - ReleaseSemaphore(_wholeScreenMutex, 1, NULL); } -byte *ScummWeb::ScummService::ScummWrapper::NativeScummWrapperGraphics::GetWholeScreenBufferRaw(int &width, int &height, int &bufferSize) { +byte *ScummWeb::ScummService::ScummWrapper::NativeScummWrapperGraphics::GetWholeScreenBufferRaw() { byte *cpyWholeScreenBuffer = new byte[GetWholeScreenBufferLength()]; memcpy(cpyWholeScreenBuffer, _wholeScreenBufferNoMouse, GetWholeScreenBufferLength()); - width = _width; - height = _height; - bufferSize = GetWholeScreenBufferLength(); - return cpyWholeScreenBuffer; } @@ -441,11 +512,10 @@ ScummWeb::ScummService::ScummWrapper::ScreenBuffer ScummWeb::ScummService::Scumm ScummWeb::ScummService::ScummWrapper::ScreenBuffer screenBuffer; int screenBufferLength = w * h; - ScreenCacheAddResult screenCacheAddResult = _screenCache.AddScreenToCache((byte *)buf, screenBufferLength); + //ScreenCacheAddResult screenCacheAddResult = _screenCache.AddScreenToCache((byte *)buf, screenBufferLength); - screenBuffer.buffer = screenCacheAddResult.firstTimeAdded ? (byte *)buf : nullptr; - screenBuffer.screenBufferHash = screenCacheAddResult.hash; - screenBuffer.length = screenCacheAddResult.firstTimeAdded ? screenBufferLength : 0; + screenBuffer.buffer = (byte *)buf; + screenBuffer.length = screenBufferLength; screenBuffer.ignoreColour = isMouseUpdate ? _cliMouse.keyColor : DO_NOT_IGNORE_ANY_COLOR; screenBuffer.x = x; screenBuffer.y = y; @@ -453,14 +523,10 @@ ScummWeb::ScummService::ScummWrapper::ScreenBuffer ScummWeb::ScummService::Scumm screenBuffer.w = w; screenBuffer.paletteBuffer = nullptr; screenBuffer.paletteBufferLength = 0; - - if (!_paletteManager->haveSeenPalette(paletteHash)) { - const char *paletteBuffer = _paletteManager->getPalette(paletteHash); - screenBuffer.paletteBuffer = (byte *)paletteBuffer; - screenBuffer.paletteBufferLength = strlen(paletteBuffer); - } + const char *paletteBuffer = _paletteManager->getPalette(paletteHash); + screenBuffer.paletteBuffer = (byte *)paletteBuffer; + screenBuffer.paletteBufferLength = strlen(paletteBuffer); screenBuffer.paletteHash = paletteHash; - _paletteManager->registerSeenPalette(paletteHash); return screenBuffer; } @@ -482,3 +548,34 @@ ScummWeb::ScummService::ScummWrapper::ScreenBuffer ScummWeb::ScummService::Scumm void ScummWeb::ScummService::ScummWrapper::NativeScummWrapperGraphics::InitScreen() { memset(_wholeScreenBufferNoMouse, 0, GetWholeScreenBufferLength()); } + +ScummWeb::ScummService::ScummWrapper::MinMaxDimensions ScummWeb::ScummService::ScummWrapper::NativeScummWrapperGraphics::GetScreenBuffersMinMaxDimensions() { + MinMaxDimensions minMaxDimensions = MinMaxDimensions(); + + if (_drawingBuffers.size() > 0) { + minMaxDimensions.lowX = _drawingBuffers[0].x; + minMaxDimensions.lowY = _drawingBuffers[0].y; + minMaxDimensions.highW = _drawingBuffers[0].x + _drawingBuffers[0].w; + minMaxDimensions.highH = _drawingBuffers[0].y + _drawingBuffers[0].h; + + for (int i = 1; i < _drawingBuffers.size() && (minMaxDimensions.lowX > 0 || minMaxDimensions.lowY > 0 || minMaxDimensions.highW < _width || minMaxDimensions.highH < _height); i++) { + if (_drawingBuffers[i].x < minMaxDimensions.lowX) { + minMaxDimensions.lowX = _drawingBuffers[i].x; + } + + if (_drawingBuffers[i].y < minMaxDimensions.lowY) { + minMaxDimensions.lowY = _drawingBuffers[i].y; + } + + if (_drawingBuffers[i].x + (_drawingBuffers[i].w - 1) > minMaxDimensions.highW) { + minMaxDimensions.highW = _drawingBuffers[i].x + _drawingBuffers[i].w; + } + + if (_drawingBuffers[i].y + (_drawingBuffers[i].h - 1) > minMaxDimensions.highH) { + minMaxDimensions.highH = _drawingBuffers[i].y + _drawingBuffers[i].h; + } + } + } + + return minMaxDimensions; +} diff --git a/ScummVmBrowser/nativeScummWrapper/nativeScummWrapperGraphics.h b/ScummVmBrowser/nativeScummWrapper/nativeScummWrapperGraphics.h index 85cbdab59294..4ffa4b0577e7 100644 --- a/ScummVmBrowser/nativeScummWrapper/nativeScummWrapperGraphics.h +++ b/ScummVmBrowser/nativeScummWrapper/nativeScummWrapperGraphics.h @@ -103,8 +103,8 @@ class NativeScummWrapperGraphics : virtual public GraphicsManager { bool screenUpdateOverlapsMouse(int x, int y, int w, int h); bool positionInRange(int x, int y); MouseState getMouseState(); - void ScheduleRedrawWholeScreen(bool emptyScreenBufferCache = false); - byte *GetWholeScreenBufferRaw(int &width, int &height, int &bufferSize); + void ScheduleRedrawWholeScreen(bool clearCache); + byte *GetWholeScreenBufferRaw(); private: ScreenCache _screenCache; @@ -126,6 +126,13 @@ class NativeScummWrapperGraphics : virtual public GraphicsManager { void InitScreen(); void ClearScreenBufferCache(); int GetWholeScreenBufferLength(); + void ScheduleRedrawWholeScreenNoLock(bool clearCache, int x = 0, int y = 0, int w = -1, int h = -1); + MinMaxDimensions GetScreenBuffersMinMaxDimensions(); + void ClearScreenBufferMemory(); + void UpdateScreenBuffersAccordingToCache(); + void UpdateScreenBuffersScreenCachesAccordingToCache(); + void UpdateScreenBufferPallettesAccordingToCache(); + byte *reduceBuffer(const void* buf, int x, int y, int w, int h, int &size); byte *_wholeScreenBufferNoMouse; HANDLE _wholeScreenMutex; Graphics::Surface _framebuffer; diff --git a/ScummVmBrowser/nativeScummWrapper/nativeScummWrapperOptions.h b/ScummVmBrowser/nativeScummWrapper/nativeScummWrapperOptions.h index f94bdd142cdf..5aaeabc337c9 100644 --- a/ScummVmBrowser/nativeScummWrapper/nativeScummWrapperOptions.h +++ b/ScummVmBrowser/nativeScummWrapper/nativeScummWrapperOptions.h @@ -4,7 +4,8 @@ namespace ScummService { namespace ScummWrapper { class NativeScummWrapperOptions { public: - int ScreenBufferCacheSize; + int screenBufferCacheSize; + int noBuffersBeforeWholeScreen; }; } // namespace ScummWrapper } // namespace ScummService diff --git a/ScummVmBrowser/nativeScummWrapper/screenCache.cpp b/ScummVmBrowser/nativeScummWrapper/screenCache.cpp index 2c9a14354997..e2ce0cd01c2c 100644 --- a/ScummVmBrowser/nativeScummWrapper/screenCache.cpp +++ b/ScummVmBrowser/nativeScummWrapper/screenCache.cpp @@ -15,7 +15,7 @@ ScummWeb::ScummService::ScummWrapper::ScreenCacheAddResult ScummWeb::ScummServic if (result.firstTimeAdded) { _addOrder.push(result.hash); - if (_addOrder.size() == _nativeScummWrapperOptions.ScreenBufferCacheSize) { + if (_addOrder.size() == _nativeScummWrapperOptions.screenBufferCacheSize) { _screenBuffers.erase(_addOrder.front()); _addOrder.pop(); } diff --git a/doc/TestList.txt b/doc/TestList.txt index 963b735742dc..c6e20abf4221 100644 --- a/doc/TestList.txt +++ b/doc/TestList.txt @@ -1,5 +1,6 @@ Leak Tests KQ3: +Watch beginning and make sure that the 'by' in the 'by Sierra' text at the beginning is not cut off Manannan's Office Take Books Save Start New Game And Restore @@ -21,6 +22,7 @@ Open Kq5 and Kq3 at the same time Steel: Make sure can open menu with F5 +Watch introduction upto 'sensors detect incoming audio source', and make sure that when that line is said that fade out works Release Build