diff --git a/.gitignore b/.gitignore index d72433e9b1..a057183f58 100644 --- a/.gitignore +++ b/.gitignore @@ -60,4 +60,6 @@ Lua-API.md # SublimeText *.sublime-project -*.sublime-workspace \ No newline at end of file +*.sublime-workspace +*.mix +*.vqa \ No newline at end of file diff --git a/OpenRA.Game/FileFormats/Format80.cs b/OpenRA.Game/FileFormats/Format80.cs index f082c7ddc8..ebe1b13501 100644 --- a/OpenRA.Game/FileFormats/Format80.cs +++ b/OpenRA.Game/FileFormats/Format80.cs @@ -60,11 +60,10 @@ namespace OpenRA.FileFormats } } - public static int DecodeInto(byte[] src, byte[] dest, int srcOffset = 0) + public static int DecodeInto(byte[] src, byte[] dest, int srcOffset = 0, bool reverse = false) { var ctx = new FastByteReader(src, srcOffset); var destIndex = 0; - while (true) { var i = ctx.ReadByte(); @@ -104,7 +103,7 @@ namespace OpenRA.FileFormats { // case 5 var count = ctx.ReadWord(); - var srcIndex = ctx.ReadWord(); + var srcIndex = reverse ? destIndex - ctx.ReadWord() : ctx.ReadWord(); if (srcIndex >= destIndex) throw new NotImplementedException("srcIndex >= destIndex {0} {1}".F(srcIndex, destIndex)); @@ -115,7 +114,7 @@ namespace OpenRA.FileFormats { // case 3 var count = count3 + 3; - var srcIndex = ctx.ReadWord(); + var srcIndex = reverse ? destIndex - ctx.ReadWord() : ctx.ReadWord(); if (srcIndex >= destIndex) throw new NotImplementedException("srcIndex >= destIndex {0} {1}".F(srcIndex, destIndex)); @@ -174,10 +173,13 @@ namespace OpenRA.FileFormats // Command 4: Repeat byte n times ms.WriteByte(0xFE); + // Low byte ms.WriteByte((byte)(repeatCount & 0xFF)); + // High byte ms.WriteByte((byte)(repeatCount >> 8)); + // Value to repeat ms.WriteByte(src[offset]); diff --git a/OpenRA.Game/FileFormats/VqaReader.cs b/OpenRA.Game/FileFormats/VqaReader.cs index feb505ed72..f8ca8a77ff 100644 --- a/OpenRA.Game/FileFormats/VqaReader.cs +++ b/OpenRA.Game/FileFormats/VqaReader.cs @@ -25,16 +25,24 @@ namespace OpenRA.FileFormats ushort numColors; ushort blockWidth; ushort blockHeight; - byte cbParts; + byte chunkBufferParts; int2 blocks; - UInt32[] offsets; + uint[] offsets; uint[] palette; + uint videoFlags; // if 0x10 is set the video is a 16 bit hq video (ts and later) + int sampleRate; + int sampleBits; + int audioChannels; // Stores a list of subpixels, referenced by the VPTZ chunk byte[] cbf; byte[] cbp; - int cbChunk = 0; - int cbOffset = 0; + byte[] cbfBuffer; + byte[] fileBuffer = new byte[256000]; // Buffer for loading file subchunks, the maximum chunk size of a file is not defined + int maxCbfzSize = 256000; // and the header definition for the size of the biggest chunks (color data) isn't accurate. But 256k is large enough for all TS videos(< 200k). + int vtprSize = 0; + int currentChunkBuffer = 0; + int chunkBufferOffset = 0; // Top half contains block info, bottom half contains references to cbf array byte[] origData; @@ -42,9 +50,14 @@ namespace OpenRA.FileFormats // Final frame output uint[,] frameData; byte[] audioData; // audio for this frame: 22050Hz 16bit mono pcm, uncompressed. + bool hasAudio; public byte[] AudioData { get { return audioData; } } public int CurrentFrame { get { return currentFrame; } } + public int SampleRate { get { return sampleRate; } } + public int SampleBits { get { return sampleBits; } } + public int AudioChannels { get { return audioChannels; } } + public bool HasAudio { get { return hasAudio; } } public VqaReader(Stream stream) { @@ -53,14 +66,14 @@ namespace OpenRA.FileFormats // Decode FORM chunk if (stream.ReadASCII(4) != "FORM") throw new InvalidDataException("Invalid vqa (invalid FORM section)"); - /*var length = */ stream.ReadUInt32(); + /*var length = */stream.ReadUInt32(); if (stream.ReadASCII(8) != "WVQAVQHD") throw new InvalidDataException("Invalid vqa (not WVQAVQHD)"); - /* var length = */stream.ReadUInt32(); + /*var length2 = */stream.ReadUInt32(); /*var version = */stream.ReadUInt16(); - /*var flags = */stream.ReadUInt16(); + videoFlags = stream.ReadUInt16(); Frames = stream.ReadUInt16(); Width = stream.ReadUInt16(); Height = stream.ReadUInt16(); @@ -68,7 +81,7 @@ namespace OpenRA.FileFormats blockWidth = stream.ReadUInt8(); blockHeight = stream.ReadUInt8(); Framerate = stream.ReadUInt8(); - cbParts = stream.ReadUInt8(); + chunkBufferParts = stream.ReadUInt8(); blocks = new int2(Width / blockWidth, Height / blockHeight); numColors = stream.ReadUInt16(); @@ -77,18 +90,34 @@ namespace OpenRA.FileFormats /*var unknown2 = */stream.ReadUInt32(); // Audio - /*var freq = */stream.ReadUInt16(); - /*var channels = */stream.ReadByte(); - /*var bits = */stream.ReadByte(); - /*var unknown3 = */stream.ReadBytes(14); + sampleRate = stream.ReadUInt16(); + audioChannels = stream.ReadByte(); + sampleBits = stream.ReadByte(); + + /*var unknown3 =*/stream.ReadUInt32(); + /*var unknown4 =*/stream.ReadUInt16(); + /*maxCbfzSize =*/stream.ReadUInt32(); // Unreliable + + /*var unknown5 =*/stream.ReadUInt32(); var frameSize = Exts.NextPowerOf2(Math.Max(Width, Height)); - cbf = new byte[Width*Height]; - cbp = new byte[Width*Height]; - palette = new uint[numColors]; - origData = new byte[2*blocks.X*blocks.Y]; - frameData = new uint[frameSize, frameSize]; + if (IsHqVqa) + { + cbfBuffer = new byte[maxCbfzSize]; + cbf = new byte[maxCbfzSize * 3]; + origData = new byte[maxCbfzSize]; + } + else + { + cbfBuffer = new byte[Width * Height]; + cbf = new byte[Width * Height]; + cbp = new byte[Width * Height]; + origData = new byte[2 * blocks.X * blocks.Y]; + } + + palette = new uint[numColors]; + frameData = new uint[frameSize, frameSize]; var type = stream.ReadASCII(4); while (type != "FINF") { @@ -107,7 +136,7 @@ namespace OpenRA.FileFormats /*var unknown4 = */stream.ReadUInt16(); // Frame offsets - offsets = new UInt32[Frames]; + offsets = new uint[Frames]; for (var i = 0; i < Frames; i++) { offsets[i] = stream.ReadUInt32(); @@ -123,15 +152,15 @@ namespace OpenRA.FileFormats public void Reset() { - currentFrame = cbOffset = cbChunk = 0; + currentFrame = chunkBufferOffset = currentChunkBuffer = 0; LoadFrame(); } void CollectAudioData() { - var ms = new MemoryStream(); + var audio1 = new MemoryStream(); // left channel / mono + var audio2 = new MemoryStream(); // right channel var adpcmIndex = 0; - var compressed = false; for (var i = 0; i < Frames; i++) { @@ -141,19 +170,37 @@ namespace OpenRA.FileFormats while (stream.Position < end) { var type = stream.ReadASCII(4); + if (type == "SN2J") + { + var jmp = int2.Swap(stream.ReadUInt32()); + stream.Seek(jmp, SeekOrigin.Current); + type = stream.ReadASCII(4); + } + var length = int2.Swap(stream.ReadUInt32()); switch (type) { case "SND0": case "SND2": - var rawAudio = stream.ReadBytes((int)length); - ms.Write(rawAudio); - compressed = (type == "SND2"); - break; + if (audioChannels == 1) + { + var rawAudio = stream.ReadBytes((int)length); + audio1.Write(rawAudio); + } + else + { + var rawAudio = stream.ReadBytes((int)length / 2); + audio1.Write(rawAudio); + rawAudio = stream.ReadBytes((int)length / 2); + audio2.Write(rawAudio); + } + + compressed = type == "SND2"; + break; default: stream.ReadBytes((int)length); - break; + break; } // Chunks are aligned on even bytes; advance by a byte if the next one is null @@ -161,7 +208,39 @@ namespace OpenRA.FileFormats } } - audioData = (compressed) ? AudLoader.LoadSound(ms.ToArray(), ref adpcmIndex) : ms.ToArray(); + if (audioChannels == 1) + { + audioData = compressed ? AudLoader.LoadSound(audio1.ToArray(), ref adpcmIndex) : audio1.ToArray(); + } + else + { + byte[] leftData, rightData; + if (!compressed) + { + leftData = audio1.ToArray(); + rightData = audio2.ToArray(); + } + else + { + adpcmIndex = 0; + leftData = AudLoader.LoadSound(audio1.ToArray(), ref adpcmIndex); + adpcmIndex = 0; + rightData = AudLoader.LoadSound(audio2.ToArray(), ref adpcmIndex); + } + + audioData = new byte[rightData.Length + leftData.Length]; + var rightIndex = 0; + var leftIndex = 0; + for (var i = 0; i < audioData.Length;) + { + audioData[i++] = leftData[leftIndex++]; + audioData[i++] = leftData[leftIndex++]; + audioData[i++] = rightData[rightIndex++]; + audioData[i++] = rightData[rightIndex++]; + } + } + + hasAudio = audioData.Length > 0; } public void AdvanceFrame() @@ -177,22 +256,45 @@ namespace OpenRA.FileFormats // Seek to the start of the frame stream.Seek(offsets[currentFrame], SeekOrigin.Begin); - var end = (currentFrame < Frames - 1) ? offsets[currentFrame+1] : stream.Length; + var end = (currentFrame < Frames - 1) ? offsets[currentFrame + 1] : stream.Length; while (stream.Position < end) { var type = stream.ReadASCII(4); - var length = int2.Swap(stream.ReadUInt32()); - - switch(type) + var length = 0U; + if (type == "SN2J") + { + var jmp = int2.Swap(stream.ReadUInt32()); + stream.Seek(jmp, SeekOrigin.Current); + type = stream.ReadASCII(4); + if (type == "SND2") + { + length = int2.Swap(stream.ReadUInt32()); + stream.Seek(length, SeekOrigin.Current); + type = stream.ReadASCII(4); + } + else + throw new NotSupportedException(); + } + + length = int2.Swap(stream.ReadUInt32()); + + switch (type) { case "VQFR": DecodeVQFR(stream); break; + case "\0VQF": + stream.ReadByte(); + DecodeVQFR(stream); + break; + case "VQFL": + DecodeVQFR(stream, "VQFL"); + break; default: // Don't parse sound here. stream.ReadBytes((int)length); - break; + break; } // Chunks are aligned on even bytes; advance by a byte if the next one is null @@ -201,7 +303,7 @@ namespace OpenRA.FileFormats } // VQA Frame - public void DecodeVQFR(Stream s) + public void DecodeVQFR(Stream s, string parentType = "VQFR") { while (true) { @@ -210,11 +312,37 @@ namespace OpenRA.FileFormats var type = s.ReadASCII(4); var subchunkLength = (int)int2.Swap(s.ReadUInt32()); - switch(type) + switch (type) { // Full frame-modifier case "CBFZ": - Format80.DecodeInto(s.ReadBytes(subchunkLength), cbf); + var decodeMode = s.Peek() == 0; + s.Read(fileBuffer, 0, subchunkLength); + Array.Clear(cbf, 0, cbf.Length); + Array.Clear(cbfBuffer, 0, cbfBuffer.Length); + var decodeCount = 0; + decodeCount = Format80.DecodeInto(fileBuffer, cbfBuffer, decodeMode ? 1 : 0, decodeMode); + if ((videoFlags & 0x10) == 16) + { + var p = 0; + for (var i = 0; i < decodeCount; i += 2) + { + var packed = cbfBuffer[i + 1] << 8 | cbfBuffer[i]; + /* 15 bit 0 + 0rrrrrgg gggbbbbb + HI byte LO byte*/ + cbf[p++] = (byte)((packed & 0x7C00) >> 7); + cbf[p++] = (byte)((packed & 0x3E0) >> 2); + cbf[p++] = (byte)((packed & 0x1f) << 3); + } + } + else + { + cbf = cbfBuffer; + } + + if (parentType == "VQFL") + return; break; case "CBF0": cbf = s.ReadBytes(subchunkLength); @@ -224,20 +352,20 @@ namespace OpenRA.FileFormats case "CBP0": case "CBPZ": // Partial buffer is full; dump and recreate - if (cbChunk == cbParts) + if (currentChunkBuffer == chunkBufferParts) { if (type == "CBP0") cbf = (byte[])cbp.Clone(); else Format80.DecodeInto(cbp, cbf); - cbOffset = cbChunk = 0; + chunkBufferOffset = currentChunkBuffer = 0; } var bytes = s.ReadBytes(subchunkLength); - bytes.CopyTo(cbp,cbOffset); - cbOffset += subchunkLength; - cbChunk++; + bytes.CopyTo(cbp, chunkBufferOffset); + chunkBufferOffset += subchunkLength; + currentChunkBuffer++; break; // Palette @@ -249,13 +377,28 @@ namespace OpenRA.FileFormats var b = (byte)(s.ReadUInt8() << 2); palette[i] = (uint)((255 << 24) | (r << 16) | (g << 8) | b); } + break; // Frame data case "VPTZ": Format80.DecodeInto(s.ReadBytes(subchunkLength), origData); + // This is the last subchunk return; + case "VPRZ": + Array.Clear(origData, 0, origData.Length); + s.Read(fileBuffer, 0, subchunkLength); + if (fileBuffer[0] != 0) + vtprSize = Format80.DecodeInto(fileBuffer, origData); + else + Format80.DecodeInto(fileBuffer, origData, 1, true); + return; + case "VPTR": + Array.Clear(origData, 0, origData.Length); + s.Read(origData, 0, subchunkLength); + vtprSize = subchunkLength; + return; default: throw new InvalidDataException("Unknown sub-chunk {0}".F(type)); } @@ -267,19 +410,76 @@ namespace OpenRA.FileFormats void DecodeFrameData() { cachedFrame = currentFrame; - for (var y = 0; y < blocks.Y; y++) - for (var x = 0; x < blocks.X; x++) + if (IsHqVqa) + { + /* The VP?? chunks of the video file contains an array of instructions for + * how the blocks of the finished frame will be filled with color data blocks + * contained in the CBF? chunks. + */ + var p = 0; + for (var y = 0; y < blocks.Y;) { - var px = origData[x + y*blocks.X]; - var mod = origData[x + (y + blocks.Y)*blocks.X]; - for (var j = 0; j < blockHeight; j++) - for (var i = 0; i < blockWidth; i++) + for (var x = 0; x < blocks.X;) + { + if (y >= blocks.Y) + break; + + // The first 3 bits of the short determine the type of instruction with the rest being one or two parameters. + var val = (int)origData[p++]; + val |= origData[p++] << 8; + var para_A = val & 0x1fff; + var para_B1 = val & 0xFF; + var para_B2 = (((val / 256) & 0x1f) + 1) * 2; + switch (val >> 13) { - var cbfi = (mod*256 + px)*8 + j*blockWidth + i; - var color = (mod == 0x0f) ? px : cbf[cbfi]; - frameData[y*blockHeight + j, x*blockWidth + i] = palette[color]; + case 0: + x += para_A; + break; + case 1: + WriteBlock(para_B1, para_B2, ref x, ref y); + break; + case 2: + WriteBlock(para_B1, 1, ref x, ref y); + for (var i = 0; i < para_B2; i++) + WriteBlock(origData[p++], 1, ref x, ref y); + break; + case 3: + WriteBlock(para_A, 1, ref x, ref y); + break; + case 5: + WriteBlock(para_A, origData[p++], ref x, ref y); + break; + default: + throw new NotSupportedException(); } + } + + y++; } + + if (p != vtprSize) + throw new IndexOutOfRangeException(); + } + else + { + for (var y = 0; y < blocks.Y; y++) + { + for (var x = 0; x < blocks.X; x++) + { + var px = origData[x + y * blocks.X]; + var mod = origData[x + (y + blocks.Y) * blocks.X]; + for (var j = 0; j < blockHeight; j++) + { + for (var i = 0; i < blockWidth; i++) + { + var cbfi = (mod * 256 + px) * 8 + j * blockWidth + i; + var color = (mod == 0x0f) ? px : cbf[cbfi]; + frameData[y * blockHeight + j, x * blockWidth + i] = palette[color]; + } + } + } + } + } } public uint[,] FrameData @@ -292,5 +492,33 @@ namespace OpenRA.FileFormats return frameData; } } + + bool IsHqVqa { get { return (videoFlags & 0x10) == 16; } } + + void WriteBlock(int blockNumber, int count, ref int x, ref int y) + { + for (var i = 0; i < count; i++) + { + var frameX = x * blockWidth; + var frameY = y * blockHeight; + var offset = blockNumber * blockHeight * blockWidth * 3; + for (var by = 0; by < blockHeight; by++) + for (var bx = 0; bx < blockWidth; bx++) + { + var p = (bx + by * blockWidth) * 3; + + frameData[frameY + by, frameX + bx] = (uint)(0xFF << 24 | cbf[offset + p] << 16 | cbf[offset + p + 1] << 8 | cbf[offset + p + 2]); + } + + x++; + if (x >= blocks.X) + { + x = 0; + y++; + if (y >= blocks.Y && i != count - 1) + throw new IndexOutOfRangeException(); + } + } + } } } diff --git a/OpenRA.Game/Sound/Sound.cs b/OpenRA.Game/Sound/Sound.cs index 535976a279..2cdc423ea0 100644 --- a/OpenRA.Game/Sound/Sound.cs +++ b/OpenRA.Game/Sound/Sound.cs @@ -43,7 +43,7 @@ namespace OpenRA return LoadWave(new WavLoader(s)); using (var s = GlobalFileSystem.Open(filename)) - return LoadSoundRaw(AudLoader.LoadSound(s)); + return LoadSoundRaw(AudLoader.LoadSound(s), 1, 16, 22050); } static ISoundSource LoadWave(WavLoader wave) @@ -51,9 +51,9 @@ namespace OpenRA return soundEngine.AddSoundSourceFromMemory(wave.RawOutput, wave.Channels, wave.BitsPerSample, wave.SampleRate); } - static ISoundSource LoadSoundRaw(byte[] rawData) + static ISoundSource LoadSoundRaw(byte[] rawData, int channels, int sampleBits, int sampleRate) { - return soundEngine.AddSoundSourceFromMemory(rawData, 1, 16, 22050); + return soundEngine.AddSoundSourceFromMemory(rawData, channels, sampleBits, sampleRate); } static ISoundEngine CreateEngine(string engine) @@ -120,9 +120,9 @@ namespace OpenRA public static ISound PlayToPlayer(Player player, string name) { return Play(player, name, true, WPos.Zero, 1); } public static ISound PlayToPlayer(Player player, string name, WPos pos) { return Play(player, name, false, pos, 1); } - public static void PlayVideo(byte[] raw) + public static void PlayVideo(byte[] raw, int channels, int sampleBits, int sampleRate) { - rawSource = LoadSoundRaw(raw); + rawSource = LoadSoundRaw(raw, channels, sampleBits, sampleRate); video = soundEngine.Play2D(rawSource, false, true, WPos.Zero, InternalSoundVolume, false); } @@ -356,7 +356,7 @@ namespace OpenRA if (!string.IsNullOrEmpty(name) && (p == null || p == p.World.LocalPlayer)) soundEngine.Play2D(sounds[name], false, relative, pos, - (InternalSoundVolume * volumeModifier), attenuateVolume); + InternalSoundVolume * volumeModifier, attenuateVolume); return true; } diff --git a/OpenRA.Game/Widgets/VqaPlayerWidget.cs b/OpenRA.Game/Widgets/VqaPlayerWidget.cs index 10731a1d27..8831a9ddad 100644 --- a/OpenRA.Game/Widgets/VqaPlayerWidget.cs +++ b/OpenRA.Game/Widgets/VqaPlayerWidget.cs @@ -65,13 +65,20 @@ namespace OpenRA.Widgets videoSheet.Texture.ScaleFilter = TextureScaleFilter.Linear; videoSheet.Texture.SetData(video.FrameData); - videoSprite = new Sprite(videoSheet, new Rectangle(0, 0, video.Width, video.Height), TextureChannel.Alpha); - var scale = Math.Min(RenderBounds.Width / video.Width, RenderBounds.Height / (video.Height * AspectRatio)); - videoOrigin = new float2(RenderBounds.X + (RenderBounds.Width - scale * video.Width) / 2, RenderBounds.Y + (RenderBounds.Height - scale * AspectRatio * video.Height) / 2); + videoSprite = new Sprite(videoSheet, + new Rectangle( + 0, + 0, + video.Width, + video.Height), + TextureChannel.Alpha); + + var scale = Math.Min((float)RenderBounds.Width / video.Width, (float)RenderBounds.Height / video.Height * AspectRatio); + videoOrigin = new float2(RenderBounds.X + (RenderBounds.Width - scale * video.Width) / 2, RenderBounds.Y + (RenderBounds.Height - scale * video.Height * AspectRatio) / 2); // Round size to integer pixels. Round up to be consistent with the scale calcuation. - videoSize = new float2((int)Math.Ceiling(video.Width * scale), (int)Math.Ceiling(video.Height * scale * AspectRatio)); + videoSize = new float2((int)Math.Ceiling(video.Width * scale), (int)Math.Ceiling(video.Height * AspectRatio * scale)); if (!DrawOverlay) return; @@ -94,23 +101,35 @@ namespace OpenRA.Widgets if (!stopped && !paused) { - var nextFrame = (int)float2.Lerp(0, video.Frames, Sound.VideoSeekPosition * invLength); + var nextFrame = 0; + if (video.HasAudio) + nextFrame = (int)float2.Lerp(0, video.Frames, Sound.VideoSeekPosition * invLength); + else + nextFrame = video.CurrentFrame + 1; + if (nextFrame > video.Frames) { Stop(); return; } + var skippedFrames = 0; while (nextFrame > video.CurrentFrame) { video.AdvanceFrame(); - if (nextFrame == video.CurrentFrame) - videoSprite.sheet.Texture.SetData(video.FrameData); + videoSprite.sheet.Texture.SetData(video.FrameData); + skippedFrames++; } + + if (skippedFrames > 1) + Log.Write("perf", "VqaPlayer : {0} skipped {1} frames at position {2}", cachedVideo, skippedFrames, video.CurrentFrame); } - Game.Renderer.RgbaSpriteRenderer.DrawSprite(videoSprite, videoOrigin, videoSize); - + Game.Renderer.RgbaSpriteRenderer.DrawSprite( + videoSprite, + videoOrigin, + videoSize); + if (DrawOverlay) Game.Renderer.RgbaSpriteRenderer.DrawSprite(overlaySprite, videoOrigin, videoSize); } @@ -141,7 +160,7 @@ namespace OpenRA.Widgets onComplete = after; if (stopped) - Sound.PlayVideo(video.AudioData); + Sound.PlayVideo(video.AudioData, video.AudioChannels, video.SampleBits, video.SampleRate); else Sound.PlayVideo(); diff --git a/OpenRA.Mods.Common/Widgets/Logic/AssetBrowserLogic.cs b/OpenRA.Mods.Common/Widgets/Logic/AssetBrowserLogic.cs index 544a55990a..90c63657e4 100644 --- a/OpenRA.Mods.Common/Widgets/Logic/AssetBrowserLogic.cs +++ b/OpenRA.Mods.Common/Widgets/Logic/AssetBrowserLogic.cs @@ -21,6 +21,10 @@ namespace OpenRA.Mods.Common.Widgets.Logic { public class AssetBrowserLogic { + static readonly string[] AllowedExtensions = { ".shp", ".r8", "tmp", ".tem", ".des", ".sno", ".int", ".jun", ".vqa" }; + + readonly World world; + Widget panel; TextFieldWidget filenameInput; @@ -39,10 +43,6 @@ namespace OpenRA.Mods.Common.Widgets.Logic bool isVideoLoaded = false; int currentFrame; - readonly World world; - - static readonly string[] AllowedExtensions = { ".shp", ".r8", "tmp", ".tem", ".des", ".sno", ".int", ".jun", ".vqa" }; - [ObjectCreator.UseCtor] public AssetBrowserLogic(Widget widget, Action onExit, World world) { @@ -112,7 +112,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic var frameContainer = panel.GetOrNull("FRAME_SELECTOR"); if (frameContainer != null) - frameContainer.IsVisible = () => (currentSprites != null && currentSprites.Length > 1) || (player != null && player.Video != null && player.Video.Frames > 1); + frameContainer.IsVisible = () => (currentSprites != null && currentSprites.Length > 1) || (isVideoLoaded && player != null && player.Video != null && player.Video.Frames > 1); frameSlider = panel.Get("FRAME_SLIDER"); if (frameSlider != null) @@ -301,9 +301,11 @@ namespace OpenRA.Mods.Common.Widgets.Logic player = panel.Get("PLAYER"); currentFilename = filename; player.Load(filename); + player.DrawOverlay = false; isVideoLoaded = true; frameSlider.MaximumValue = (float)player.Video.Frames - 1; frameSlider.Ticks = 0; + return true; } else { @@ -330,7 +332,6 @@ namespace OpenRA.Mods.Common.Widgets.Logic // TODO: Re-enable "All Packages" once list generation is done in a background thread // var sources = new[] { (IFolder)null }.Concat(GlobalFileSystem.MountedFolders); - var sources = GlobalFileSystem.MountedFolders; dropdown.ShowDropDown("LABEL_DROPDOWN_TEMPLATE", 280, sources, setupItem); return true; @@ -344,7 +345,6 @@ namespace OpenRA.Mods.Common.Widgets.Logic // TODO: This is too slow to run in the main thread // var files = AssetSource != null ? AssetSource.AllFileNames() : // GlobalFileSystem.MountedFolders.SelectMany(f => f.AllFileNames()); - if (assetSource == null) return; diff --git a/mods/cnc/chrome/assetbrowser.yaml b/mods/cnc/chrome/assetbrowser.yaml index 5978575875..52532c8dd9 100644 --- a/mods/cnc/chrome/assetbrowser.yaml +++ b/mods/cnc/chrome/assetbrowser.yaml @@ -103,6 +103,7 @@ Container@ASSETBROWSER_PANEL: VqaPlayer@PLAYER: Width: PARENT_RIGHT Height: PARENT_BOTTOM + AspectRatio: 1 Container@FRAME_SELECTOR: X: 190 Y: 395 diff --git a/mods/ra/chrome/assetbrowser.yaml b/mods/ra/chrome/assetbrowser.yaml index a211ead23f..f206ff7c7b 100644 --- a/mods/ra/chrome/assetbrowser.yaml +++ b/mods/ra/chrome/assetbrowser.yaml @@ -98,6 +98,7 @@ Background@ASSETBROWSER_PANEL: VqaPlayer@PLAYER: Width: PARENT_RIGHT Height: PARENT_BOTTOM + AspectRatio: 1 Container@FRAME_SELECTOR: X: 190 Y: 425