Partially reconstructed decompression algorithm

amazfit_cor
Valeriy Mironov 2018-05-13 17:42:18 +03:00
parent d29691e0ee
commit 09f7ef180d
4 changed files with 117 additions and 10 deletions

View File

@ -6,6 +6,8 @@ namespace Resources
{ {
public class Compression public class Compression
{ {
private const int SectionSize = 4096;
private static readonly byte[] SomeData = new byte[16] {4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0};
private static readonly Logger Logger = LogManager.GetCurrentClassLogger(); private static readonly Logger Logger = LogManager.GetCurrentClassLogger();
class Header class Header
@ -41,7 +43,7 @@ namespace Resources
public Stream Decompress() public Stream Decompress()
{ {
var output = new MemoryStream(); var output = new MemoryStream();
var buffer = new byte[5120]; var sourceBuffer = new byte[SectionSize];
var offset = (uint)0; var offset = (uint)0;
@ -49,33 +51,32 @@ namespace Resources
{ {
var header = Header.Read(_reader); var header = Header.Read(_reader);
if (header.DecompressedLength == 4096) if (header.DecompressedLength == SectionSize)
{ {
Logger.Debug("Compression block header read at offset 0x{0:X}", offset); Logger.Debug("Compression block header read at offset 0x{0:X}", offset);
Logger.Debug("Flags 0x{0:X}, Compressed {1}, Decompressed {2}, LengthSize {3}", Logger.Debug("Flags 0x{0:X}, Compressed {1}, Decompressed {2}, LengthSize {3}",
header.Flags, header.CompressedLength, header.DecompressedLength, header.LengthSize); header.Flags, header.CompressedLength, header.DecompressedLength, header.LengthSize);
_reader.Read(buffer, 0, (int)header.CompressedLength - 9); _reader.Read(sourceBuffer, 0, (int)header.CompressedLength - Header.Size);
offset += header.CompressedLength; offset += header.CompressedLength;
if (header.IsCompressed) if (header.IsCompressed)
{ {
// Here should be decompression var decompressed = DecompressBuffer(sourceBuffer);
output.Write(decompressed, 0, (int)header.DecompressedLength);
} }
else else
output.Write(buffer, 0, (int)header.CompressedLength); output.Write(sourceBuffer, 0, (int)header.CompressedLength - Header.Size);
} }
else else
{ {
var latestBlock = _streamSize - offset; var latestBlock = _streamSize - offset;
Logger.Debug("Latest block read at offset 0x{0:X}, size: {1}", offset, latestBlock); Logger.Debug("Latest block read at offset 0x{0:X}, size: {1}", offset, latestBlock);
_reader.Read(buffer, 0, (int)(latestBlock - Header.Size)); _reader.Read(sourceBuffer, 0, (int)(latestBlock - Header.Size));
output.Write(header.Buffer, 0, Header.Size); output.Write(header.Buffer, 0, Header.Size);
output.Write(buffer, 0, (int)(latestBlock - Header.Size)); output.Write(sourceBuffer, 0, (int)(latestBlock - Header.Size));
break; break;
} }
} }
@ -84,5 +85,94 @@ namespace Resources
return output; return output;
} }
private byte[] DecompressBuffer(byte[] srcBuffer)
{
var dstBuffer = new byte[SectionSize];
var data = (uint)1;
var dstOffset = 0;
var srcOffset = 0;
while (true)
{
while (true)
{
if (data == 1)
{
data = BitConverter.ToUInt32(srcBuffer, srcOffset);
srcOffset += 4;
}
var link = BitConverter.ToUInt32(srcBuffer, srcOffset);
if (data << 31 == 0)
break;
data = data >> 1;
uint dupOffset, dupLength;
if (link << 30 == 0)
{
dupOffset = (uint)((byte)link >> 2);
dupLength = 3;
srcOffset++;
}
else if ((link & 2) == 0)
{
dupOffset = (uint)((ushort)link >> 2);
dupLength = 3;
srcOffset += 2;
}
else if (link << 31 == 0)
{
dupOffset = (uint)((ushort)link >> 6);
dupLength = ((link >> 2) & 0xF) + 3;
srcOffset += 2;
}
else if ((link & 0x7F) == 3)
{
dupOffset = link >> 15;
dupLength = ((link >> 7) & 0xFF) + 3;
srcOffset += 4;
}
else
{
dupOffset = (link >> 7) & 0x1FFFF;
dupLength = ((link >> 2) & 0x1F) + 2;
srcOffset += 3;
}
Array.Copy(dstBuffer, dstOffset - dupOffset, dstBuffer, dstOffset, dupLength);
dstOffset += (int)dupLength;
}
if (dstOffset >= SectionSize - 11)
break;
var length = SomeData[data & 0xf];
Array.Copy(srcBuffer, srcOffset, dstBuffer, dstOffset, 4);
data >>= length;
dstOffset += length;
srcOffset += length;
}
while (dstOffset <= SectionSize - 1)
{
if (data == 1)
{
data = 0x80000000;
srcOffset += 4;
}
data >>= 1;
var tmp = srcBuffer[srcOffset];
srcOffset++;
dstBuffer[dstOffset] = tmp;
dstOffset++;
}
return dstBuffer;
}
} }
} }

View File

@ -32,6 +32,10 @@ namespace Resources.Image
throw new ArgumentException("Image signature doesn't match."); throw new ArgumentException("Image signature doesn't match.");
ReadHeader(); ReadHeader();
if (_paletteColors > 256)
throw new ArgumentException(
"Too many palette colors.");
if (_paletteColors > 0) if (_paletteColors > 0)
ReadPalette(); ReadPalette();
else if (_bitsPerPixel == 16 || _bitsPerPixel == 24 || _bitsPerPixel == 32) else if (_bitsPerPixel == 16 || _bitsPerPixel == 24 || _bitsPerPixel == 32)

View File

@ -43,7 +43,14 @@ namespace Resources
_stream.Seek(realOffset, SeekOrigin.Begin); _stream.Seek(realOffset, SeekOrigin.Begin);
} }
Logger.Debug("Reading image {0}...", i); Logger.Debug("Reading image {0}...", i);
images.Add(new Image.Reader(_stream).Read()); try
{
images.Add(new Image.Reader(_stream).Read());
}
catch
{
Logger.Warn("Error on reading image {0}", i);
}
} }
return images; return images;
} }

View File

@ -209,6 +209,12 @@ namespace WatchFace
using (var stream = File.OpenRead(inputFileName)) using (var stream = File.OpenRead(inputFileName))
{ {
var decompressedStream = new Compression(stream).Decompress(); var decompressedStream = new Compression(stream).Decompress();
using (var wStream = File.OpenWrite(Path.Combine(outputDirectory, $"{inputFileName}.res"))) {
decompressedStream.CopyTo(wStream);
wStream.Flush();
}
decompressedStream.Seek(0, SeekOrigin.Begin);
resDescriptor = FileReader.Read(decompressedStream); resDescriptor = FileReader.Read(decompressedStream);
} }