Created
August 1, 2024 23:34
-
-
Save parzivail/47ad3b922d2efbd669a496ced704687f to your computer and use it in GitHub Desktop.
Myst (Windows) WDIB to BMP
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System.Collections; | |
using System.Net; | |
namespace MystKit; | |
class Program | |
{ | |
static void Main(string[] args) | |
{ | |
foreach (var path in Directory.GetFiles(@"D:\myst\MYST_DAT", "*WDIB*.bin")) | |
{ | |
Console.WriteLine(path); | |
using var fs = File.OpenRead(path); | |
var rle = new RunLengthCompressionStream(fs); | |
using var fsOut = File.Create(Path.Combine(@"D:\myst\MYST_BMP", Path.GetFileNameWithoutExtension(path) + ".bmp")); | |
rle.CopyTo(fsOut); | |
} | |
} | |
} | |
public class RunLengthCompressionStream : Stream | |
{ | |
private readonly uint _length; | |
private readonly Stream _source; | |
private readonly RingBuffer _ringBuffer; | |
private long _position; | |
private byte _commandByte; | |
private int _commandBit = 8; | |
private int _runLength; | |
private int _runRingOffset; | |
public override bool CanRead => true; | |
public override bool CanSeek => false; | |
public override bool CanWrite => false; | |
public override long Length => _length; | |
public override long Position | |
{ | |
get => _position; | |
set => throw new NotSupportedException(); | |
} | |
public RunLengthCompressionStream(Stream source, int ringBufferSize = 0x400) | |
{ | |
_source = source; | |
_ringBuffer = new RingBuffer(ringBufferSize); | |
Span<byte> lengthBytes = stackalloc byte[4]; | |
source.ReadExactly(lengthBytes); | |
_length = BitConverter.ToUInt32(lengthBytes); | |
} | |
public override void Flush() | |
{ | |
throw new NotSupportedException(); | |
} | |
public override int Read(byte[] buffer, int offset, int count) | |
{ | |
Span<byte> runDataBuffer = stackalloc byte[2]; | |
var startOffset = offset; | |
while (offset < startOffset + count) | |
{ | |
if (_runLength > 0) | |
{ | |
// If we have a non-zero run length, continue reading the run | |
_runLength--; | |
buffer[offset++] = _ringBuffer.Pop(_runRingOffset); | |
_runRingOffset++; | |
} | |
else | |
{ | |
if (_commandBit == 8) | |
{ | |
// The current command byte has been exhausted, read a new one | |
var commandByte = _source.ReadByte(); | |
if (commandByte == -1) | |
break; | |
_commandByte = (byte)commandByte; | |
_commandBit = 0; | |
} | |
// Execute the next command | |
var command = _commandByte & (1 << _commandBit); | |
_commandBit++; | |
if (command == 0) | |
{ | |
// read new run | |
if (_source.Read(runDataBuffer) != runDataBuffer.Length) | |
break; | |
_runLength = ((runDataBuffer[0] & 0b11111100) >> 2) + 3; | |
_runRingOffset = ((runDataBuffer[0] & 0b11) << 8) | runDataBuffer[1]; | |
_runRingOffset += 0x42; | |
} | |
else | |
{ | |
// read absolute byte | |
var rawByte = _source.ReadByte(); | |
if (rawByte == -1) | |
break; | |
_ringBuffer.Push((byte)rawByte); | |
buffer[offset++] = (byte)rawByte; | |
} | |
} | |
} | |
var bytesRead = offset - startOffset; | |
_position += bytesRead; | |
return bytesRead; | |
} | |
public override long Seek(long offset, SeekOrigin origin) | |
{ | |
throw new NotSupportedException(); | |
} | |
public override void SetLength(long value) | |
{ | |
throw new NotSupportedException(); | |
} | |
public override void Write(byte[] buffer, int offset, int count) | |
{ | |
throw new NotSupportedException(); | |
} | |
} | |
public class RingBuffer | |
{ | |
private readonly byte[] _data; | |
private int _cursor; | |
public byte this[int offset] | |
{ | |
get => _data[offset % _data.Length]; | |
set => _data[offset % _data.Length] = value; | |
} | |
public RingBuffer(int size) | |
{ | |
_data = new byte[size]; | |
} | |
public void Push(byte value) | |
{ | |
this[_cursor++] = value; | |
_cursor %= _data.Length; | |
} | |
public byte Pop(int index) | |
{ | |
var value = this[index]; | |
Push(value); | |
return value; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment