diff --git a/Aaru.Compression/ADC.cs b/Aaru.Compression/ADC.cs new file mode 100644 index 000000000..ad0f22f32 --- /dev/null +++ b/Aaru.Compression/ADC.cs @@ -0,0 +1,175 @@ +// /*************************************************************************** +// Aaru Data Preservation Suite +// ---------------------------------------------------------------------------- +// +// Filename : ADC.cs +// Author(s) : Natalia Portillo +// +// Component : Compression algorithms. +// +// --[ Description ] ---------------------------------------------------------- +// +// Decompresses Apple Data Compression. +// +// --[ License ] -------------------------------------------------------------- +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// +// ---------------------------------------------------------------------------- +// Copyright © 2016-2021 Natalia Portillo +// ****************************************************************************/ + +using System; +using System.Runtime.CompilerServices; + +namespace Aaru.Compression +{ + /// Implements the Apple version of RLE + public static class ADC + { + const int PLAIN = 1; + const int TWO_BYTE = 2; + const int THREE_BYTE = 3; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + static int GetChunkType(byte byt) => (byt & 0x80) == 0x80 + ? PLAIN + : (byt & 0x40) == 0x40 + ? THREE_BYTE + : TWO_BYTE; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + static int GetChunkSize(byte byt) => GetChunkType(byt) switch + { + PLAIN => (byt & 0x7F) + 1, + TWO_BYTE => ((byt & 0x3F) >> 2) + 3, + THREE_BYTE => (byt & 0x3F) + 4, + _ => -1 + }; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + static int GetOffset(ReadOnlySpan chunk) => GetChunkType(chunk[0]) switch + { + PLAIN => 0, + TWO_BYTE => ((chunk[0] & 0x03) << 8) + chunk[1], + THREE_BYTE => (chunk[1] << 8) + chunk[2], + _ => -1 + }; + + /// Decompresses a byte buffer that's compressed with ADC + /// Compressed buffer + /// Buffer to hold decompressed data + /// How many bytes are stored on + [MethodImpl(MethodImplOptions.AggressiveOptimization)] + public static int DecodeBuffer(byte[] source, byte[] destination) + { + int inputPosition = 0; + int chunkSize; + int offset; + int chunkType; + int outPosition = 0; + Span temp = stackalloc byte[3]; + + while(inputPosition < source.Length) + { + byte readByte = source[inputPosition++]; + + chunkType = GetChunkType(readByte); + + switch(chunkType) + { + case PLAIN: + chunkSize = GetChunkSize(readByte); + + if(outPosition + chunkSize > destination.Length) + goto finished; + + Array.Copy(source, inputPosition, destination, outPosition, chunkSize); + outPosition += chunkSize; + inputPosition += chunkSize; + + break; + case TWO_BYTE: + chunkSize = GetChunkSize(readByte); + temp[0] = readByte; + temp[1] = source[inputPosition++]; + offset = GetOffset(temp); + + if(outPosition + chunkSize > destination.Length) + goto finished; + + if(offset == 0) + { + byte lastByte = destination[outPosition - 1]; + + for(int i = 0; i < chunkSize; i++) + { + destination[outPosition] = lastByte; + outPosition++; + } + } + else + { + for(int i = 0; i < chunkSize; i++) + { + destination[outPosition] = destination[outPosition - offset - 1]; + outPosition++; + } + } + + break; + case THREE_BYTE: + chunkSize = GetChunkSize(readByte); + temp[0] = readByte; + temp[1] = source[inputPosition++]; + temp[2] = source[inputPosition++]; + offset = GetOffset(temp); + + if(outPosition + chunkSize > destination.Length) + goto finished; + + if(offset == 0) + { + byte lastByte = destination[outPosition - 1]; + + for(int i = 0; i < chunkSize; i++) + { + destination[outPosition] = lastByte; + outPosition++; + } + } + else + { + for(int i = 0; i < chunkSize; i++) + { + destination[outPosition] = destination[outPosition - offset - 1]; + outPosition++; + } + } + + break; + } + } + + finished: + + return outPosition; + } + } +} \ No newline at end of file diff --git a/Aaru.Compression/Aaru.Compression.csproj b/Aaru.Compression/Aaru.Compression.csproj index 1bd804b13..02d77991c 100644 --- a/Aaru.Compression/Aaru.Compression.csproj +++ b/Aaru.Compression/Aaru.Compression.csproj @@ -59,10 +59,10 @@ true - - - - + + + + @@ -70,6 +70,7 @@ + diff --git a/Aaru.Images/NDIF/Read.cs b/Aaru.Images/NDIF/Read.cs index 3e8e9ece3..b4e53a31a 100644 --- a/Aaru.Images/NDIF/Read.cs +++ b/Aaru.Images/NDIF/Read.cs @@ -42,7 +42,6 @@ using Aaru.Console; using Aaru.Helpers; using Claunia.Encoding; using Claunia.RsrcFork; -using SharpCompress.Compressors.ADC; using Version = Resources.Version; namespace Aaru.DiscImages @@ -372,9 +371,8 @@ namespace Aaru.DiscImages { case CHUNK_TYPE_ADC: { - Stream decStream = new ADCStream(cmpMs); byte[] tmpBuffer = new byte[_bufferSize]; - realSize = decStream.Read(tmpBuffer, 0, (int)_bufferSize); + realSize = ADC.DecodeBuffer(cmpBuffer, tmpBuffer); data = new byte[realSize]; Array.Copy(tmpBuffer, 0, data, 0, realSize); diff --git a/Aaru.Images/UDIF/Read.cs b/Aaru.Images/UDIF/Read.cs index 672b0517b..c7d6c93ae 100644 --- a/Aaru.Images/UDIF/Read.cs +++ b/Aaru.Images/UDIF/Read.cs @@ -44,7 +44,6 @@ using Claunia.PropertyList; using Claunia.RsrcFork; using Ionic.BZip2; using Ionic.Zlib; -using SharpCompress.Compressors.ADC; using Version = Resources.Version; #pragma warning disable 612 @@ -501,10 +500,6 @@ namespace Aaru.DiscImages switch(readChunk.type) { - case CHUNK_TYPE_ADC: - decStream = new ADCStream(cmpMs); - - break; case CHUNK_TYPE_ZLIB: decStream = new ZlibStream(cmpMs, CompressionMode.Decompress); @@ -513,8 +508,9 @@ namespace Aaru.DiscImages decStream = new BZip2InputStream(cmpMs, false); break; + case CHUNK_TYPE_ADC: case CHUNK_TYPE_RLE: break; - default: return ErrorNumber.NotImplemented; + default: return ErrorNumber.NotImplemented; } #if DEBUG @@ -526,7 +522,6 @@ namespace Aaru.DiscImages switch(readChunk.type) { - case CHUNK_TYPE_ADC: case CHUNK_TYPE_ZLIB: case CHUNK_TYPE_BZIP: tmpBuffer = new byte[_buffersize]; @@ -543,6 +538,13 @@ namespace Aaru.DiscImages _chunkCache.Add(chunkStartSector, data); _currentChunkCacheSize += (uint)realSize; + break; + case CHUNK_TYPE_ADC: + tmpBuffer = new byte[_buffersize]; + realSize = ADC.DecodeBuffer(cmpBuffer, tmpBuffer); + data = new byte[realSize]; + Array.Copy(tmpBuffer, 0, data, 0, realSize); + break; case CHUNK_TYPE_RLE: tmpBuffer = new byte[_buffersize];