-
Notifications
You must be signed in to change notification settings - Fork 5
/
LzxDecoderStream.cs
147 lines (121 loc) · 3.78 KB
/
LzxDecoderStream.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
// MIT License - Copyright (C) The Mono.Xna Team
// This file is subject to the terms and conditions defined in
// file 'LICENSE.txt', which is part of this source code package.
using Microsoft.Xna.Framework.Content;
using System;
using System.IO;
namespace MonoSound{
internal class LzxDecoderStream : Stream
{
LzxDecoder dec;
MemoryStream decompressedStream;
public LzxDecoderStream(Stream input, int decompressedSize, int compressedSize)
{
dec = new LzxDecoder(16);
// TODO: Rewrite using block decompression like Lz4DecoderStream
Decompress(input, decompressedSize, compressedSize);
}
// Decompress into MemoryStream
private void Decompress(Stream stream, int decompressedSize, int compressedSize)
{
//thanks to ShinAli (https://bitbucket.org/alisci01/xnbdecompressor)
// default window size for XNB encoded files is 64Kb (need 16 bits to represent it)
decompressedStream = new MemoryStream(decompressedSize);
long startPos = stream.Position;
long pos = startPos;
while (pos - startPos < compressedSize)
{
// the compressed stream is seperated into blocks that will decompress
// into 32Kb or some other size if specified.
// normal, 32Kb output blocks will have a short indicating the size
// of the block before the block starts
// blocks that have a defined output will be preceded by a byte of value
// 0xFF (255), then a short indicating the output size and another
// for the block size
// all shorts for these cases are encoded in big endian order
int hi = stream.ReadByte();
int lo = stream.ReadByte();
int block_size = (hi << 8) | lo;
int frame_size = 0x8000; // frame size is 32Kb by default
// does this block define a frame size?
if (hi == 0xFF)
{
hi = lo;
lo = (byte)stream.ReadByte();
frame_size = (hi << 8) | lo;
hi = (byte)stream.ReadByte();
lo = (byte)stream.ReadByte();
block_size = (hi << 8) | lo;
pos += 5;
}
else
pos += 2;
// either says there is nothing to decode
if (block_size == 0 || frame_size == 0)
break;
dec.Decompress(stream, block_size, decompressedStream, frame_size);
pos += block_size;
// reset the position of the input just incase the bit buffer
// read in some unused bytes
stream.Seek(pos, SeekOrigin.Begin);
}
if (decompressedStream.Position != decompressedSize)
{
throw new ContentLoadException("Decompression failed.");
}
decompressedStream.Seek(0, SeekOrigin.Begin);
}
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
if(disposing)
{
decompressedStream.Dispose();
}
dec = null;
decompressedStream = null;
}
#region Stream internals
public override int Read(byte[] buffer, int offset, int count)
{
return decompressedStream.Read(buffer, offset, count);
}
public override bool CanRead
{
get { return true; }
}
public override bool CanSeek
{
get { return false; }
}
public override bool CanWrite
{
get { return false; }
}
public override void Flush()
{
}
public override long Length
{
get { throw new NotSupportedException(); }
}
public override long Position
{
get { throw new NotSupportedException(); }
set { throw new NotSupportedException(); }
}
public override long Seek(long offset, SeekOrigin origin)
{
throw new NotImplementedException();
}
public override void SetLength(long value)
{
throw new NotImplementedException();
}
public override void Write(byte[] buffer, int offset, int count)
{
throw new NotImplementedException();
}
#endregion
}
}