Add a .wsa file reader.

This commit is contained in:
Matthias Mailänder
2020-04-25 21:08:11 +02:00
committed by reaperrr
parent 590ab88c45
commit a02737107e
7 changed files with 217 additions and 7 deletions

View File

@@ -0,0 +1,136 @@
#region Copyright & License Information
/*
* Copyright 2007-2021 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version. For more
* information, see COPYING.
*/
#endregion
using System;
using System.IO;
using OpenRA.Video;
namespace OpenRA.Mods.Cnc.FileFormats
{
public class WsaReader : IVideo
{
readonly Stream stream;
public ushort Frames { get { return frameCount; } }
public byte Framerate { get { return 1; } }
public ushort Width { get { return width; } }
public ushort Height { get { return height; } }
readonly ushort frameCount;
readonly ushort width;
readonly ushort height;
readonly uint[] palette;
readonly uint[] frameOffsets;
uint[,] coloredFrameData;
public uint[,] FrameData { get { return coloredFrameData; } }
int currentFrame;
byte[] previousFrameData;
byte[] currentFrameData;
public byte[] AudioData { get { return null; } }
public int CurrentFrame { get { return currentFrame; } }
public int SampleRate { get { return 0; } }
public int SampleBits { get { return 0; } }
public int AudioChannels { get { return 0; } }
public bool HasAudio { get { return false; } }
public WsaReader(Stream stream)
{
this.stream = stream;
frameCount = stream.ReadUInt16();
var x = stream.ReadUInt16();
var y = stream.ReadUInt16();
width = stream.ReadUInt16();
height = stream.ReadUInt16();
var delta = stream.ReadUInt16() + 37;
var flags = stream.ReadUInt16();
frameOffsets = new uint[frameCount + 2];
for (var i = 0; i < frameOffsets.Length; i++)
frameOffsets[i] = stream.ReadUInt32();
if (flags == 1)
{
palette = new uint[256];
for (var i = 0; i < palette.Length; i++)
{
var r = (byte)(stream.ReadByte() << 2);
var g = (byte)(stream.ReadByte() << 2);
var b = (byte)(stream.ReadByte() << 2);
// Replicate high bits into the (currently zero) low bits.
r |= (byte)(r >> 6);
g |= (byte)(g >> 6);
b |= (byte)(b >> 6);
palette[i] = (uint)((255 << 24) | (r << 16) | (g << 8) | b);
}
for (var i = 0; i < frameOffsets.Length; i++)
frameOffsets[i] += 768;
}
Reset();
}
public void Reset()
{
currentFrame = 0;
previousFrameData = null;
LoadFrame();
}
public void AdvanceFrame()
{
previousFrameData = currentFrameData;
currentFrame++;
LoadFrame();
}
void LoadFrame()
{
if (currentFrame >= frameCount)
return;
stream.Seek(frameOffsets[currentFrame], SeekOrigin.Begin);
var dataLength = frameOffsets[currentFrame + 1] - frameOffsets[currentFrame];
var rawData = StreamExts.ReadBytes(stream, (int)dataLength);
var intermediateData = new byte[width * height];
// Format80 decompression
LCWCompression.DecodeInto(rawData, intermediateData);
// and Format40 decompression
currentFrameData = new byte[width * height];
if (previousFrameData == null)
Array.Clear(currentFrameData, 0, currentFrameData.Length);
else
Array.Copy(previousFrameData, currentFrameData, currentFrameData.Length);
XORDeltaCompression.DecodeInto(intermediateData, currentFrameData, 0);
var c = 0;
var frameSize = Exts.NextPowerOf2(Math.Max(width, height));
coloredFrameData = new uint[frameSize, frameSize];
for (var y = 0; y < height; y++)
for (var x = 0; x < width; x++)
coloredFrameData[y, x] = palette[currentFrameData[c++]];
}
}
}

View File

@@ -0,0 +1,66 @@
#region Copyright & License Information
/*
* Copyright 2007-2021 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version. For more
* information, see COPYING.
*/
#endregion
using System.IO;
using System.Linq;
using OpenRA.Mods.Cnc.FileFormats;
using OpenRA.Video;
namespace OpenRA.Mods.Cnc.VideoLoaders
{
public class WsaLoader : IVideoLoader
{
public bool TryParseVideo(Stream s, out IVideo video)
{
video = null;
if (!IsWsa(s))
return false;
video = new WsaReader(s);
return true;
}
bool IsWsa(Stream s)
{
var start = s.Position;
var frames = s.ReadUInt16();
var x = s.ReadUInt16();
var y = s.ReadUInt16();
var width = s.ReadUInt16();
var height = s.ReadUInt16();
if (width <= 0 || height <= 0)
return false;
var delta = s.ReadUInt16() + 37;
var flags = s.ReadUInt16();
var offsets = new uint[frames + 2];
for (var i = 0; i < offsets.Length; i++)
offsets[i] = s.ReadUInt32();
if (flags == 1)
{
var palette = StreamExts.ReadBytes(s, 768);
for (var i = 0; i < offsets.Length; i++)
offsets[i] += 768;
}
s.Position = start;
return s.Length == offsets.Last();
}
}
}

View File

@@ -195,7 +195,7 @@ namespace OpenRA.Mods.Common.Widgets
return;
onComplete = after;
if (stopped)
if (stopped && video.HasAudio)
Game.Sound.PlayVideo(video.AudioData, video.AudioChannels, video.SampleBits, video.SampleRate);
else
Game.Sound.PlayVideo();

View File

@@ -24,6 +24,7 @@ Packages:
~scores2.mix
~scores-covertops.mix
~transit.mix
~general.mix
cnc|bits/snow.mix
cnc|bits
cnc|bits/jungle
@@ -217,7 +218,7 @@ SoundFormats: Aud, Wav
SpriteFormats: ShpTD, TmpTD, ShpTS, TmpRA
VideoFormats: Vqa
VideoFormats: Vqa, Wsa
TerrainFormat: DefaultTerrain
@@ -232,7 +233,7 @@ SpriteSequenceFormat: ClassicTilesetSpecificSpriteSequence
ModelSequenceFormat: PlaceholderModelSequence
AssetBrowser:
SupportedExtensions: .shp, .tem, .des, .sno, .jun, .vqa
SupportedExtensions: .shp, .tem, .des, .sno, .jun, .vqa, .wsa
GameSpeeds:
slowest:

View File

@@ -21,6 +21,9 @@ allied: Red Alert 95 (Allied Disc, English)
^SupportDir|Content/ra/v2/conquer.mix:
Offset: 236
Length: 2177047
^SupportDir|Content/ra/v2/general.mix:
Offset: 2239848
Length: 14932344
^SupportDir|Content/ra/v2/interior.mix:
Offset: 17172192
Length: 247425
@@ -391,4 +394,4 @@ allied-linux: Red Alert 95 (Allied Disc, English)
Length: 309406
^SupportDir|Content/ra/v2/temperat.mix:
Offset: 453566435
Length: 1038859
Length: 1038859

View File

@@ -21,6 +21,9 @@ soviet: Red Alert 95 (Soviet Disc, English)
^SupportDir|Content/ra/v2/conquer.mix:
Offset: 236
Length: 2177047
^SupportDir|Content/ra/v2/general.mix:
Offset: 2239848
Length: 14932344
^SupportDir|Content/ra/v2/interior.mix:
Offset: 17172192
Length: 247425
@@ -414,4 +417,4 @@ soviet-linux: Red Alert 95 (Soviet Disc, English)
Length: 1006778
^SupportDir|Content/ra/v2/temperat.mix:
Offset: 499538555
Length: 1038859
Length: 1038859

View File

@@ -30,6 +30,7 @@ Packages:
~expand2.mix
~hires1.mix
~desert.mix
~general.mix
ra|bits
ra|bits/desert
ra|bits/scripts
@@ -223,7 +224,7 @@ SoundFormats: Aud, Wav
SpriteFormats: ShpD2, ShpTD, TmpRA, TmpTD, ShpTS
VideoFormats: Vqa
VideoFormats: Vqa, Wsa
TerrainFormat: DefaultTerrain
@@ -237,7 +238,7 @@ SpriteSequenceFormat: ClassicTilesetSpecificSpriteSequence
ModelSequenceFormat: PlaceholderModelSequence
AssetBrowser:
SupportedExtensions: .shp, .tmp, .tem, .des, .sno, .int, .vqa
SupportedExtensions: .shp, .tmp, .tem, .des, .sno, .int, .vqa, .wsa
GameSpeeds:
slowest: