Add a .wsa file reader.
This commit is contained in:
committed by
reaperrr
parent
590ab88c45
commit
a02737107e
136
OpenRA.Mods.Cnc/FileFormats/WsaReader.cs
Normal file
136
OpenRA.Mods.Cnc/FileFormats/WsaReader.cs
Normal 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++]];
|
||||
}
|
||||
}
|
||||
}
|
||||
66
OpenRA.Mods.Cnc/VideoLoaders/WsaLoader.cs
Normal file
66
OpenRA.Mods.Cnc/VideoLoaders/WsaLoader.cs
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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:
|
||||
|
||||
Reference in New Issue
Block a user