mirror of
https://github.com/aaru-dps/Aaru.git
synced 2025-12-16 19:24:25 +00:00
179 lines
7.4 KiB
C#
179 lines
7.4 KiB
C#
// /***************************************************************************
|
|
// Aaru Data Preservation Suite
|
|
// ----------------------------------------------------------------------------
|
|
//
|
|
// Filename : ADC.cs
|
|
// Author(s) : Natalia Portillo <claunia@claunia.com>
|
|
//
|
|
// 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-2022 Natalia Portillo
|
|
// ****************************************************************************/
|
|
|
|
namespace Aaru.Compression;
|
|
|
|
using System;
|
|
using System.Runtime.CompilerServices;
|
|
using System.Runtime.InteropServices;
|
|
|
|
/// <summary>Implements the Apple version of RLE</summary>
|
|
public static class ADC
|
|
{
|
|
const int PLAIN = 1;
|
|
const int TWO_BYTE = 2;
|
|
const int THREE_BYTE = 3;
|
|
/// <summary>Set to <c>true</c> if this algorithm is supported, <c>false</c> otherwise.</summary>
|
|
public static bool IsSupported => true;
|
|
|
|
[DllImport("libAaru.Compression.Native", SetLastError = true)]
|
|
static extern int AARU_adc_decode_buffer(byte[] dst_buffer, int dst_size, byte[] src_buffer, int src_size);
|
|
|
|
[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<byte> chunk) => GetChunkType(chunk[0]) switch
|
|
{
|
|
PLAIN => 0,
|
|
TWO_BYTE => ((chunk[0] & 0x03) << 8) + chunk[1],
|
|
THREE_BYTE => (chunk[1] << 8) + chunk[2],
|
|
_ => -1
|
|
};
|
|
|
|
/// <summary>Decompresses a byte buffer that's compressed with ADC</summary>
|
|
/// <param name="source">Compressed buffer</param>
|
|
/// <param name="destination">Buffer to hold decompressed data</param>
|
|
/// <returns>How many bytes are stored on <paramref name="destination" /></returns>
|
|
[MethodImpl(MethodImplOptions.AggressiveOptimization)]
|
|
public static int DecodeBuffer(byte[] source, byte[] destination)
|
|
{
|
|
if(Native.IsSupported)
|
|
return AARU_adc_decode_buffer(destination, destination.Length, source, source.Length);
|
|
|
|
var inputPosition = 0;
|
|
int chunkSize;
|
|
int offset;
|
|
int chunkType;
|
|
var outPosition = 0;
|
|
Span<byte> 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(var i = 0; i < chunkSize; i++)
|
|
{
|
|
destination[outPosition] = lastByte;
|
|
outPosition++;
|
|
}
|
|
}
|
|
else
|
|
for(var 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(var i = 0; i < chunkSize; i++)
|
|
{
|
|
destination[outPosition] = lastByte;
|
|
outPosition++;
|
|
}
|
|
}
|
|
else
|
|
for(var i = 0; i < chunkSize; i++)
|
|
{
|
|
destination[outPosition] = destination[outPosition - offset - 1];
|
|
outPosition++;
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
finished:
|
|
|
|
return outPosition;
|
|
}
|
|
} |