mirror of
https://github.com/aaru-dps/Aaru.Compression.Native.git
synced 2026-07-08 18:06:12 +00:00
Enhance RAR decompression logic to handle filter boundaries and improve output management
This commit is contained in:
38
rar/rar50.c
38
rar/rar50.c
@@ -495,24 +495,36 @@ AARU_EXPORT int AARU_CALL rar50_decompress(const uint8_t *in_buf, size_t in_len,
|
||||
/* Main decompression loop */
|
||||
while(ctx.out_pos < ctx.out_size)
|
||||
{
|
||||
int64_t target = (int64_t)ctx.out_size;
|
||||
int64_t pos = (int64_t)ctx.out_pos;
|
||||
|
||||
/* If a filter is pending, stop expansion at its start */
|
||||
if(ctx.filters) target = ctx.filters->start;
|
||||
int64_t nextfilterstart = (int64_t)ctx.out_size;
|
||||
if(ctx.filters) nextfilterstart = ctx.filters->start;
|
||||
|
||||
/* Also limit to out_size */
|
||||
if(target > (int64_t)ctx.out_size) target = (int64_t)ctx.out_size;
|
||||
if(pos == nextfilterstart)
|
||||
{
|
||||
/* We're at a filter boundary — expand through the filter data */
|
||||
int64_t target = nextfilterstart + (int64_t)ctx.filters->length;
|
||||
|
||||
/* Expand LZSS data up to target */
|
||||
if(expand_to_position(&ctx, target) != 0) goto fail;
|
||||
if(expand_to_position(&ctx, target) != 0) goto fail;
|
||||
if(flush_output(&ctx, ctx.lzss.position) != 0) goto fail;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Expand up to the next filter or end of output, with window protection */
|
||||
int64_t target = pos + 0x40000;
|
||||
int64_t winedge = rar_lzss_next_window_edge(&ctx.lzss, pos);
|
||||
if(target > winedge) target = winedge;
|
||||
if(target > nextfilterstart) target = nextfilterstart;
|
||||
if(target > (int64_t)ctx.out_size) target = (int64_t)ctx.out_size;
|
||||
|
||||
/* Flush available data to out_buf (applying filters) */
|
||||
if(flush_output(&ctx, ctx.lzss.position) != 0) goto fail;
|
||||
if(expand_to_position(&ctx, target) != 0) goto fail;
|
||||
if(flush_output(&ctx, ctx.lzss.position) != 0) goto fail;
|
||||
|
||||
/* If we're stuck (no progress), either we finished or there's an error */
|
||||
if(ctx.lzss.position <= (int64_t)ctx.out_pos && ctx.islastblock &&
|
||||
rar_bs_bit_offset(&ctx.bs) >= ctx.blockbitend)
|
||||
break;
|
||||
/* If no progress was made, check if we're truly done */
|
||||
if(ctx.lzss.position <= pos && ctx.islastblock &&
|
||||
rar_bs_bit_offset(&ctx.bs) >= ctx.blockbitend)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
*out_len = ctx.out_pos;
|
||||
|
||||
@@ -138,6 +138,15 @@ file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/data/rar30_default.bin
|
||||
file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/data/rar50_default.bin
|
||||
DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/data/)
|
||||
|
||||
file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/data/rar50_m3.bin
|
||||
DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/data/)
|
||||
|
||||
file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/data/rar50_m4.bin
|
||||
DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/data/)
|
||||
|
||||
file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/data/rar50_m5.bin
|
||||
DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/data/)
|
||||
|
||||
# 'Google_Tests_run' is the target name
|
||||
# 'test1.cpp tests2.cpp' are source files with tests
|
||||
add_executable(tests_run apple_rle.cpp crc32.c crc32.h adc.cpp bzip2.cpp lzip.cpp lzfse.cpp zstd.cpp lzma.cpp flac.cpp lz4.cpp
|
||||
|
||||
BIN
tests/data/rar50_m3.bin
Normal file
BIN
tests/data/rar50_m3.bin
Normal file
Binary file not shown.
BIN
tests/data/rar50_m4.bin
Normal file
BIN
tests/data/rar50_m4.bin
Normal file
Binary file not shown.
BIN
tests/data/rar50_m5.bin
Normal file
BIN
tests/data/rar50_m5.bin
Normal file
Binary file not shown.
@@ -206,3 +206,135 @@ TEST_F(rar50Fixture, rar50_decompress)
|
||||
free(outBuf);
|
||||
EXPECT_EQ(crc, (uint32_t)EXPECTED_CRC32);
|
||||
}
|
||||
|
||||
/* ── RAR 5.0 method 3/4/5 (Calgary obj2, with filters) ── */
|
||||
|
||||
#define RAR50_OBJ2_CRC32 0x3ae33007
|
||||
#define RAR50_OBJ2_ORIGSIZE 246814
|
||||
#define RAR50_OBJ2_WINSIZE 1048576 /* 1MB dictionary */
|
||||
|
||||
class rar50M3Fixture : public ::testing::Test
|
||||
{
|
||||
protected:
|
||||
uint8_t *buffer;
|
||||
size_t srcLen;
|
||||
|
||||
void SetUp() override
|
||||
{
|
||||
char cwd[PATH_MAX];
|
||||
getcwd(cwd, PATH_MAX);
|
||||
char path[PATH_MAX];
|
||||
snprintf(path, PATH_MAX, "%s/data/rar50_m3.bin", cwd);
|
||||
|
||||
FILE *f = fopen(path, "rb");
|
||||
ASSERT_NE(f, nullptr) << "Cannot open " << path;
|
||||
fseek(f, 0, SEEK_END);
|
||||
srcLen = (size_t)ftell(f);
|
||||
fseek(f, 0, SEEK_SET);
|
||||
buffer = (uint8_t *)malloc(srcLen);
|
||||
ASSERT_NE(buffer, nullptr);
|
||||
fread(buffer, 1, srcLen, f);
|
||||
fclose(f);
|
||||
}
|
||||
|
||||
void TearDown() override { free(buffer); }
|
||||
};
|
||||
|
||||
TEST_F(rar50M3Fixture, rar50_m3_decompress)
|
||||
{
|
||||
size_t destLen = RAR50_OBJ2_ORIGSIZE;
|
||||
uint8_t *outBuf = (uint8_t *)malloc(RAR50_OBJ2_ORIGSIZE);
|
||||
ASSERT_NE(outBuf, nullptr);
|
||||
|
||||
int err = rar50_decompress(buffer, srcLen, outBuf, &destLen, RAR50_OBJ2_WINSIZE);
|
||||
EXPECT_EQ(err, 0);
|
||||
EXPECT_EQ(destLen, (size_t)RAR50_OBJ2_ORIGSIZE);
|
||||
|
||||
uint32_t crc = crc32_data(outBuf, RAR50_OBJ2_ORIGSIZE);
|
||||
free(outBuf);
|
||||
EXPECT_EQ(crc, (uint32_t)RAR50_OBJ2_CRC32);
|
||||
}
|
||||
|
||||
class rar50M4Fixture : public ::testing::Test
|
||||
{
|
||||
protected:
|
||||
uint8_t *buffer;
|
||||
size_t srcLen;
|
||||
|
||||
void SetUp() override
|
||||
{
|
||||
char cwd[PATH_MAX];
|
||||
getcwd(cwd, PATH_MAX);
|
||||
char path[PATH_MAX];
|
||||
snprintf(path, PATH_MAX, "%s/data/rar50_m4.bin", cwd);
|
||||
|
||||
FILE *f = fopen(path, "rb");
|
||||
ASSERT_NE(f, nullptr) << "Cannot open " << path;
|
||||
fseek(f, 0, SEEK_END);
|
||||
srcLen = (size_t)ftell(f);
|
||||
fseek(f, 0, SEEK_SET);
|
||||
buffer = (uint8_t *)malloc(srcLen);
|
||||
ASSERT_NE(buffer, nullptr);
|
||||
fread(buffer, 1, srcLen, f);
|
||||
fclose(f);
|
||||
}
|
||||
|
||||
void TearDown() override { free(buffer); }
|
||||
};
|
||||
|
||||
TEST_F(rar50M4Fixture, rar50_m4_decompress)
|
||||
{
|
||||
size_t destLen = RAR50_OBJ2_ORIGSIZE;
|
||||
uint8_t *outBuf = (uint8_t *)malloc(RAR50_OBJ2_ORIGSIZE);
|
||||
ASSERT_NE(outBuf, nullptr);
|
||||
|
||||
int err = rar50_decompress(buffer, srcLen, outBuf, &destLen, RAR50_OBJ2_WINSIZE);
|
||||
EXPECT_EQ(err, 0);
|
||||
EXPECT_EQ(destLen, (size_t)RAR50_OBJ2_ORIGSIZE);
|
||||
|
||||
uint32_t crc = crc32_data(outBuf, RAR50_OBJ2_ORIGSIZE);
|
||||
free(outBuf);
|
||||
EXPECT_EQ(crc, (uint32_t)RAR50_OBJ2_CRC32);
|
||||
}
|
||||
|
||||
class rar50M5Fixture : public ::testing::Test
|
||||
{
|
||||
protected:
|
||||
uint8_t *buffer;
|
||||
size_t srcLen;
|
||||
|
||||
void SetUp() override
|
||||
{
|
||||
char cwd[PATH_MAX];
|
||||
getcwd(cwd, PATH_MAX);
|
||||
char path[PATH_MAX];
|
||||
snprintf(path, PATH_MAX, "%s/data/rar50_m5.bin", cwd);
|
||||
|
||||
FILE *f = fopen(path, "rb");
|
||||
ASSERT_NE(f, nullptr) << "Cannot open " << path;
|
||||
fseek(f, 0, SEEK_END);
|
||||
srcLen = (size_t)ftell(f);
|
||||
fseek(f, 0, SEEK_SET);
|
||||
buffer = (uint8_t *)malloc(srcLen);
|
||||
ASSERT_NE(buffer, nullptr);
|
||||
fread(buffer, 1, srcLen, f);
|
||||
fclose(f);
|
||||
}
|
||||
|
||||
void TearDown() override { free(buffer); }
|
||||
};
|
||||
|
||||
TEST_F(rar50M5Fixture, rar50_m5_decompress)
|
||||
{
|
||||
size_t destLen = RAR50_OBJ2_ORIGSIZE;
|
||||
uint8_t *outBuf = (uint8_t *)malloc(RAR50_OBJ2_ORIGSIZE);
|
||||
ASSERT_NE(outBuf, nullptr);
|
||||
|
||||
int err = rar50_decompress(buffer, srcLen, outBuf, &destLen, RAR50_OBJ2_WINSIZE);
|
||||
EXPECT_EQ(err, 0);
|
||||
EXPECT_EQ(destLen, (size_t)RAR50_OBJ2_ORIGSIZE);
|
||||
|
||||
uint32_t crc = crc32_data(outBuf, RAR50_OBJ2_ORIGSIZE);
|
||||
free(outBuf);
|
||||
EXPECT_EQ(crc, (uint32_t)RAR50_OBJ2_CRC32);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user