mirror of
https://github.com/aaru-dps/Aaru.Compression.Native.git
synced 2025-12-16 19:24:31 +00:00
Compare commits
4 Commits
v6.0.0-alp
...
devel
| Author | SHA1 | Date | |
|---|---|---|---|
|
ce378021ba
|
|||
|
4df2934506
|
|||
|
3b91dcef7c
|
|||
|
54410bf59e
|
@@ -2,7 +2,7 @@
|
||||
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
|
||||
<metadata>
|
||||
<id>Aaru.Compression.Native</id>
|
||||
<version>6.0.0-alpha.11.3</version>
|
||||
<version>6.0.0-alpha.11.4</version>
|
||||
<description>C implementation of compression algorithms used by Aaru.</description>
|
||||
<authors>claunia</authors>
|
||||
<projectUrl>https://github.com/aaru-dps/Aaru.Compression.Native</projectUrl>
|
||||
|
||||
@@ -129,21 +129,20 @@ endif()
|
||||
add_subdirectory(3rdparty)
|
||||
|
||||
add_library("Aaru.Compression.Native" SHARED library.c apple_rle.c apple_rle.h adc.c adc.h lzip.c flac.c flac.h
|
||||
zoo/lzd.c
|
||||
zoo/lzd.h
|
||||
zoo/lzh.c
|
||||
zoo/decode.c
|
||||
zoo/huf.c
|
||||
zoo/io.c
|
||||
zoo/lh5.h
|
||||
zoo/lh5.c
|
||||
zoo/lzh.h
|
||||
zoo/ar.h
|
||||
zoo/maketbl.c
|
||||
arc/pack.c
|
||||
arc/squeeze.c
|
||||
arc/crunch.c
|
||||
arc/lzw.c)
|
||||
zoo/lzd.c zoo/lzd.h zoo/lzh.c zoo/decode.c zoo/huf.c zoo/io.c zoo/lh5.c zoo/lh5.h zoo/lzh.h zoo/ar.h zoo/maketbl.c
|
||||
arc/pack.c arc/squeeze.c arc/crunch.c arc/lzw.c
|
||||
pak/crush.c pak/distill.c pak/bitstream.c pak/bitstream.h pak/lzw.c pak/lzw.h pak/prefixcode.c
|
||||
pak/prefixcode.h
|
||||
ha/acoder.c
|
||||
ha/acoder.h
|
||||
ha/asc.c
|
||||
ha/asc.h
|
||||
ha/decompress.c
|
||||
ha/hsc.c
|
||||
ha/hsc.h
|
||||
ha/internal.h
|
||||
ha/swdict.c
|
||||
ha/swdict.h)
|
||||
|
||||
include(3rdparty/bzip2.cmake)
|
||||
include(3rdparty/flac.cmake)
|
||||
|
||||
134
ha/acoder.c
Normal file
134
ha/acoder.c
Normal file
@@ -0,0 +1,134 @@
|
||||
/***********************************************************************
|
||||
This file is part of HA, a general purpose file archiver.
|
||||
Copyright (C) 1995 Harri Hirvola
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
************************************************************************
|
||||
HA arithmetic coder
|
||||
***********************************************************************/
|
||||
|
||||
/***********************************************************************
|
||||
This file contains some small changes made by Nico de Vries (AIP-NL)
|
||||
allowing it to be compiled with Borland C++ 3.1.
|
||||
***********************************************************************/
|
||||
|
||||
/***********************************************************************
|
||||
Modified to work with memory buffers instead of files by
|
||||
Copyright (C) 2005 Natalia Portillo
|
||||
************************************************************************/
|
||||
|
||||
#include "internal.h"
|
||||
#include "acoder.h"
|
||||
#include <string.h>
|
||||
|
||||
static decompress_context_t *g_ctx = NULL;
|
||||
static U16B h, l, v;
|
||||
static S16B gpat;
|
||||
|
||||
/* Get byte from input buffer */
|
||||
static int getbyte_from_buffer(void)
|
||||
{
|
||||
if(!g_ctx || g_ctx->input.pos >= g_ctx->input.size) { return -1; }
|
||||
return g_ctx->input.data[g_ctx->input.pos++];
|
||||
}
|
||||
|
||||
/* Bit input from memory buffer */
|
||||
static int getbit_from_buffer(void)
|
||||
{
|
||||
if(!g_ctx || g_ctx->input.error) { return -1; }
|
||||
|
||||
gpat <<= 1;
|
||||
if(!(gpat & 0xff))
|
||||
{
|
||||
int byte = getbyte_from_buffer();
|
||||
if(byte < 0) { gpat = 0x100; }
|
||||
else
|
||||
{
|
||||
gpat = byte;
|
||||
gpat <<= 1;
|
||||
gpat |= 1;
|
||||
}
|
||||
}
|
||||
return (gpat & 0x100) >> 8;
|
||||
}
|
||||
|
||||
#define getbit(b) { \
|
||||
int _bit = getbit_from_buffer(); \
|
||||
if (_bit < 0) { \
|
||||
g_ctx->input.error = 1; \
|
||||
b = 0; \
|
||||
} else { \
|
||||
b |= _bit; \
|
||||
} \
|
||||
}
|
||||
|
||||
void ac_init_decode(decompress_context_t *ctx)
|
||||
{
|
||||
g_ctx = ctx;
|
||||
h = 0xffff;
|
||||
l = 0;
|
||||
gpat = 0;
|
||||
|
||||
int b1 = getbyte_from_buffer();
|
||||
int b2 = getbyte_from_buffer();
|
||||
|
||||
if(b1 < 0 || b2 < 0)
|
||||
{
|
||||
ctx->input.error = 1;
|
||||
v = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
v = (b1 << 8) | (0xff & b2);
|
||||
}
|
||||
|
||||
void ac_in(U16B low, U16B high, U16B tot)
|
||||
{
|
||||
if(!g_ctx || g_ctx->input.error) return;
|
||||
|
||||
U32B r = (U32B)(h - l) + 1;
|
||||
h = (U16B)(r * high / tot - 1) + l;
|
||||
l += (U16B)(r * low / tot);
|
||||
|
||||
while(!((h ^ l) & 0x8000))
|
||||
{
|
||||
l <<= 1;
|
||||
h <<= 1;
|
||||
h |= 1;
|
||||
v <<= 1;
|
||||
getbit(v);
|
||||
if(g_ctx->input.error) return;
|
||||
}
|
||||
|
||||
while((l & 0x4000) && !(h & 0x4000))
|
||||
{
|
||||
l <<= 1;
|
||||
l &= 0x7fff;
|
||||
h <<= 1;
|
||||
h |= 0x8001;
|
||||
v <<= 1;
|
||||
v ^= 0x8000;
|
||||
getbit(v);
|
||||
if(g_ctx->input.error) return;
|
||||
}
|
||||
}
|
||||
|
||||
U16B ac_threshold_val(U16B tot)
|
||||
{
|
||||
if(!g_ctx || g_ctx->input.error) return 0;
|
||||
|
||||
U32B r = (U32B)(h - l) + 1;
|
||||
return (U16B)((((U32B)(v - l) + 1) * tot - 1) / r);
|
||||
}
|
||||
38
ha/acoder.h
Normal file
38
ha/acoder.h
Normal file
@@ -0,0 +1,38 @@
|
||||
/***********************************************************************
|
||||
This file is part of HA, a general purpose file archiver.
|
||||
Copyright (C) 1995 Harri Hirvola
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
************************************************************************
|
||||
HA arithmetic coder
|
||||
***********************************************************************/
|
||||
|
||||
/***********************************************************************
|
||||
Modified to work with memory buffers instead of files by
|
||||
Copyright (C) 2005 Natalia Portillo
|
||||
************************************************************************/
|
||||
|
||||
#ifndef ACODER_H
|
||||
#define ACODER_H
|
||||
|
||||
#include "internal.h"
|
||||
|
||||
void ac_init_decode(decompress_context_t *ctx);
|
||||
|
||||
void ac_in(U16B low, U16B high, U16B tot);
|
||||
|
||||
U16B ac_threshold_val(U16B tot);
|
||||
|
||||
#endif /* ACODER_H */
|
||||
304
ha/asc.c
Normal file
304
ha/asc.c
Normal file
@@ -0,0 +1,304 @@
|
||||
/***********************************************************************
|
||||
This file is part of HA, a general purpose file archiver.
|
||||
Copyright (C) 1995 Harri Hirvola
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
************************************************************************
|
||||
HA ASC method
|
||||
***********************************************************************/
|
||||
|
||||
/***********************************************************************
|
||||
Modified to work with memory buffers instead of files by
|
||||
Copyright (C) 2005 Natalia Portillo
|
||||
************************************************************************/
|
||||
|
||||
#include "internal.h"
|
||||
#include "acoder.h"
|
||||
#include "swdict.h"
|
||||
#include "asc.h"
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
static decompress_context_t *g_ctx = NULL;
|
||||
|
||||
static void tabinit(U16B t[], U16B tl, U16B ival)
|
||||
{
|
||||
register U16B i, j;
|
||||
|
||||
for(i = tl; i < 2 * tl; ++i) t[i] = ival;
|
||||
for(i = tl - 1, j = (tl << 1) - 2; i; --i, j -= 2) { t[i] = t[j] + t[j + 1]; }
|
||||
}
|
||||
|
||||
static void tscale(U16B t[], U16B tl)
|
||||
{
|
||||
register U16B i, j;
|
||||
|
||||
for(i = (tl << 1) - 1; i >= tl; --i) { if(t[i] > 1) t[i] >>= 1; }
|
||||
for(i = tl - 1, j = (tl << 1) - 2; i; --i, j -= 2) { t[i] = t[j] + t[j + 1]; }
|
||||
}
|
||||
|
||||
static void tupd(U16B t[], U16B tl, U16B maxt, U16B step, U16B p)
|
||||
{
|
||||
register S16B i;
|
||||
|
||||
for(i = p + tl; i; i >>= 1) t[i] += step;
|
||||
if(t[1] >= maxt) tscale(t, tl);
|
||||
}
|
||||
|
||||
static void tzero(U16B t[], U16B tl, U16B p)
|
||||
{
|
||||
register S16B i, step;
|
||||
|
||||
for(i = p + tl, step = t[i]; i; i >>= 1) t[i] -= step;
|
||||
}
|
||||
|
||||
static void model_init(void)
|
||||
{
|
||||
register S16B i;
|
||||
|
||||
ces = CTSTEP;
|
||||
les = LTSTEP;
|
||||
ccnt = 0;
|
||||
ttcon = 0;
|
||||
npt = pmax = 1;
|
||||
for(i = 0; i < TTORD; ++i) ttab[i][0] = ttab[i][1] = TTSTEP;
|
||||
tabinit(ltab, LTCODES, 0);
|
||||
tabinit(eltab, LTCODES, 1);
|
||||
tabinit(ctab, CTCODES, 0);
|
||||
tabinit(ectab, CTCODES, 1);
|
||||
tabinit(ptab, PTCODES, 0);
|
||||
tupd(ptab, PTCODES, MAXPT, PTSTEP, 0);
|
||||
}
|
||||
|
||||
static void unpack_init(decompress_context_t *ctx)
|
||||
{
|
||||
g_ctx = ctx;
|
||||
model_init();
|
||||
ac_init_decode(ctx);
|
||||
}
|
||||
|
||||
static void ttscale(U16B con)
|
||||
{
|
||||
ttab[con][0] >>= 1;
|
||||
if(ttab[con][0] == 0) ttab[con][0] = 1;
|
||||
ttab[con][1] >>= 1;
|
||||
if(ttab[con][1] == 0) ttab[con][1] = 1;
|
||||
}
|
||||
|
||||
static void flush_output(void)
|
||||
{
|
||||
/* Output flushing is handled by the buffer management */
|
||||
}
|
||||
|
||||
int asc_unpack(decompress_context_t *ctx)
|
||||
{
|
||||
register U16B l, p, tv, i, lt;
|
||||
|
||||
swd_dinit(ctx, POSCODES);
|
||||
if(ctx->output.error) return -1;
|
||||
|
||||
unpack_init(ctx);
|
||||
if(ctx->input.error)
|
||||
{
|
||||
swd_cleanup();
|
||||
return -1;
|
||||
}
|
||||
|
||||
for(;;)
|
||||
{
|
||||
if(ctx->input.error || ctx->output.error) break;
|
||||
|
||||
tv = ac_threshold_val(ttab[ttcon][0] + ttab[ttcon][1] + 1);
|
||||
i = ttab[ttcon][0] + ttab[ttcon][1];
|
||||
|
||||
if(ttab[ttcon][0] > tv)
|
||||
{
|
||||
ac_in(0, ttab[ttcon][0], i + 1);
|
||||
ttab[ttcon][0] += TTSTEP;
|
||||
if(i >= MAXTT) ttscale(ttcon);
|
||||
ttcon = (ttcon << 1) & TTOMASK;
|
||||
|
||||
tv = ac_threshold_val(ctab[1] + ces);
|
||||
if(tv >= ctab[1])
|
||||
{
|
||||
ac_in(ctab[1], ctab[1] + ces, ctab[1] + ces);
|
||||
tv = ac_threshold_val(ectab[1]);
|
||||
for(l = 2, lt = 0;;)
|
||||
{
|
||||
if(lt + ectab[l] <= tv)
|
||||
{
|
||||
lt += ectab[l];
|
||||
++l;
|
||||
}
|
||||
if(l >= CTCODES)
|
||||
{
|
||||
l -= CTCODES;
|
||||
break;
|
||||
}
|
||||
l <<= 1;
|
||||
}
|
||||
ac_in(lt, lt + ectab[CTCODES + l], ectab[1]);
|
||||
tzero(ectab, CTCODES, l);
|
||||
if(ectab[1] != 0) ces += CTSTEP;
|
||||
else ces = 0;
|
||||
for(i = l < CPLEN ? 0 : l - CPLEN; i < (l + CPLEN >= CTCODES - 1 ? CTCODES - 1 : l + CPLEN); ++i)
|
||||
{
|
||||
if(ectab[CTCODES + i]) tupd(ectab, CTCODES, MAXCT, 1, i);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for(l = 2, lt = 0;;)
|
||||
{
|
||||
if(lt + ctab[l] <= tv)
|
||||
{
|
||||
lt += ctab[l];
|
||||
l++;
|
||||
}
|
||||
if(l >= CTCODES)
|
||||
{
|
||||
l -= CTCODES;
|
||||
break;
|
||||
}
|
||||
l <<= 1;
|
||||
}
|
||||
ac_in(lt, lt + ctab[CTCODES + l], ctab[1] + ces);
|
||||
}
|
||||
tupd(ctab, CTCODES, MAXCT, CTSTEP, l);
|
||||
if(ctab[CTCODES + l] == CCUTOFF) ces -= CTSTEP < ces ? CTSTEP : ces - 1;
|
||||
swd_dchar(l);
|
||||
if(ccnt < POSCODES) ++ccnt;
|
||||
}
|
||||
else if(i > tv)
|
||||
{
|
||||
ac_in(ttab[ttcon][0], i, i + 1);
|
||||
ttab[ttcon][1] += TTSTEP;
|
||||
if(i >= MAXTT) ttscale(ttcon);
|
||||
ttcon = ((ttcon << 1) | 1) & TTOMASK;
|
||||
|
||||
while(ccnt > pmax)
|
||||
{
|
||||
tupd(ptab, PTCODES, MAXPT, PTSTEP, npt++);
|
||||
pmax <<= 1;
|
||||
}
|
||||
|
||||
tv = ac_threshold_val(ptab[1]);
|
||||
for(p = 2, lt = 0;;)
|
||||
{
|
||||
if(lt + ptab[p] <= tv)
|
||||
{
|
||||
lt += ptab[p];
|
||||
p++;
|
||||
}
|
||||
if(p >= PTCODES)
|
||||
{
|
||||
p -= PTCODES;
|
||||
break;
|
||||
}
|
||||
p <<= 1;
|
||||
}
|
||||
ac_in(lt, lt + ptab[PTCODES + p], ptab[1]);
|
||||
tupd(ptab, PTCODES, MAXPT, PTSTEP, p);
|
||||
|
||||
if(p > 1)
|
||||
{
|
||||
for(i = 1; p; i <<= 1, --p);
|
||||
i >>= 1;
|
||||
if(i == (pmax >> 1)) l = ccnt - (pmax >> 1);
|
||||
else l = i;
|
||||
p = ac_threshold_val(l);
|
||||
ac_in(p, p + 1, l);
|
||||
p += i;
|
||||
}
|
||||
|
||||
tv = ac_threshold_val(ltab[1] + les);
|
||||
if(tv >= ltab[1])
|
||||
{
|
||||
ac_in(ltab[1], ltab[1] + les, ltab[1] + les);
|
||||
tv = ac_threshold_val(eltab[1]);
|
||||
for(l = 2, lt = 0;;)
|
||||
{
|
||||
if(lt + eltab[l] <= tv)
|
||||
{
|
||||
lt += eltab[l];
|
||||
++l;
|
||||
}
|
||||
if(l >= LTCODES)
|
||||
{
|
||||
l -= LTCODES;
|
||||
break;
|
||||
}
|
||||
l <<= 1;
|
||||
}
|
||||
ac_in(lt, lt + eltab[LTCODES + l], eltab[1]);
|
||||
tzero(eltab, LTCODES, l);
|
||||
if(eltab[1] != 0) les += LTSTEP;
|
||||
else les = 0;
|
||||
for(i = l < LPLEN ? 0 : l - LPLEN; i < (l + LPLEN >= LTCODES - 1 ? LTCODES - 1 : l + LPLEN); ++i)
|
||||
{
|
||||
if(eltab[LTCODES + i]) tupd(eltab, LTCODES, MAXLT, 1, i);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for(l = 2, lt = 0;;)
|
||||
{
|
||||
if(lt + ltab[l] <= tv)
|
||||
{
|
||||
lt += ltab[l];
|
||||
++l;
|
||||
}
|
||||
if(l >= LTCODES)
|
||||
{
|
||||
l -= LTCODES;
|
||||
break;
|
||||
}
|
||||
l <<= 1;
|
||||
}
|
||||
ac_in(lt, lt + ltab[LTCODES + l], ltab[1] + les);
|
||||
}
|
||||
tupd(ltab, LTCODES, MAXLT, LTSTEP, l);
|
||||
if(ltab[LTCODES + l] == LCUTOFF) les -= LTSTEP < les ? LTSTEP : les - 1;
|
||||
|
||||
if(l == SLCODES - 1) l = LENCODES - 1;
|
||||
else if(l >= SLCODES)
|
||||
{
|
||||
i = ac_threshold_val(LLLEN);
|
||||
ac_in(i, i + 1, LLLEN);
|
||||
l = ((l - SLCODES) << LLBITS) + i + SLCODES - 1;
|
||||
}
|
||||
l += 3;
|
||||
|
||||
if(ccnt < POSCODES)
|
||||
{
|
||||
ccnt += l;
|
||||
if(ccnt > POSCODES) ccnt = POSCODES;
|
||||
}
|
||||
swd_dpair(l, p);
|
||||
}
|
||||
else
|
||||
{
|
||||
ac_in(i, i + 1, i + 1);
|
||||
flush_output();
|
||||
swd_cleanup();
|
||||
return 0;
|
||||
}
|
||||
|
||||
if(ctx->input.error || ctx->output.error) break;
|
||||
}
|
||||
|
||||
swd_cleanup();
|
||||
return -1;
|
||||
}
|
||||
71
ha/asc.h
Normal file
71
ha/asc.h
Normal file
@@ -0,0 +1,71 @@
|
||||
/***********************************************************************
|
||||
This file is part of HA, a general purpose file archiver.
|
||||
Copyright (C) 1995 Harri Hirvola
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
************************************************************************
|
||||
HA ASC method
|
||||
***********************************************************************/
|
||||
|
||||
/***********************************************************************
|
||||
Modified to work with memory buffers instead of files by
|
||||
Copyright (C) 2005 Natalia Portillo
|
||||
************************************************************************/
|
||||
|
||||
#ifndef ASC_H
|
||||
#define ASC_H
|
||||
|
||||
#include "internal.h"
|
||||
|
||||
int asc_unpack(decompress_context_t *ctx);
|
||||
|
||||
#define POSCODES 31200
|
||||
#define SLCODES 16
|
||||
#define LLCODES 48
|
||||
#define LLLEN 16
|
||||
#define LLBITS 4
|
||||
#define LLMASK (LLLEN-1)
|
||||
#define LENCODES (SLCODES+LLCODES*LLLEN)
|
||||
#define LTCODES (SLCODES+LLCODES)
|
||||
#define CTCODES 256
|
||||
#define PTCODES 16
|
||||
#define LTSTEP 8
|
||||
#define MAXLT (750*LTSTEP)
|
||||
#define CTSTEP 1
|
||||
#define MAXCT (1000*CTSTEP)
|
||||
#define PTSTEP 24
|
||||
#define MAXPT (250*PTSTEP)
|
||||
#define TTSTEP 40
|
||||
#define MAXTT (150*TTSTEP)
|
||||
#define TTORD 4
|
||||
#define TTOMASK (TTORD-1)
|
||||
#define LCUTOFF (3*LTSTEP)
|
||||
#define CCUTOFF (3*CTSTEP)
|
||||
#define CPLEN 8
|
||||
#define LPLEN 4
|
||||
#define MINLENLIM 4096
|
||||
|
||||
static U16B ltab[2 * LTCODES];
|
||||
static U16B eltab[2 * LTCODES];
|
||||
static U16B ptab[2 * PTCODES];
|
||||
static U16B ctab[2 * CTCODES];
|
||||
static U16B ectab[2 * CTCODES];
|
||||
static U16B ttab[TTORD][2];
|
||||
static U16B ccnt, pmax, npt;
|
||||
static U16B ces;
|
||||
static U16B les;
|
||||
static U16B ttcon;
|
||||
|
||||
#endif /* ASC_H */
|
||||
70
ha/decompress.c
Normal file
70
ha/decompress.c
Normal file
@@ -0,0 +1,70 @@
|
||||
|
||||
/***********************************************************************
|
||||
Modified to work with memory buffers instead of files by
|
||||
Copyright (C) 2005 Natalia Portillo
|
||||
************************************************************************/
|
||||
|
||||
#include "internal.h"
|
||||
#include "asc.h"
|
||||
#include "hsc.h"
|
||||
#include "../library.h"
|
||||
#include <string.h>
|
||||
|
||||
int ha_algorithm_decompress(ha_algorithm_t algorithm,
|
||||
const unsigned char *in_buf,
|
||||
size_t in_len,
|
||||
unsigned char * out_buf,
|
||||
size_t * out_len)
|
||||
{
|
||||
if(!in_buf || !out_buf || !out_len || in_len == 0) { return -1; }
|
||||
|
||||
decompress_context_t ctx;
|
||||
memset(&ctx, 0, sizeof(ctx));
|
||||
|
||||
/* Initialize input buffer */
|
||||
ctx.input.data = in_buf;
|
||||
ctx.input.size = in_len;
|
||||
ctx.input.pos = 0;
|
||||
ctx.input.error = 0;
|
||||
|
||||
/* Initialize output buffer */
|
||||
ctx.output.data = out_buf;
|
||||
ctx.output.size = 0;
|
||||
ctx.output.pos = 0;
|
||||
ctx.output.max_size = *out_len;
|
||||
ctx.output.error = 0;
|
||||
|
||||
ctx.algorithm = algorithm;
|
||||
|
||||
int result;
|
||||
switch(algorithm)
|
||||
{
|
||||
case HA_ALGORITHM_ASC:
|
||||
result = asc_unpack(&ctx);
|
||||
break;
|
||||
case HA_ALGORITHM_HSC:
|
||||
result = hsc_unpack(&ctx);
|
||||
break;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
|
||||
*out_len = ctx.output.size;
|
||||
return result;
|
||||
}
|
||||
|
||||
AARU_EXPORT int AARU_CALL ha_asc_decompress(const unsigned char *in_buf,
|
||||
size_t in_len,
|
||||
unsigned char * out_buf,
|
||||
size_t * out_len)
|
||||
{
|
||||
return ha_algorithm_decompress(HA_ALGORITHM_ASC, in_buf, in_len, out_buf, out_len);
|
||||
}
|
||||
|
||||
AARU_EXPORT int AARU_CALL ha_hsc_decompress(const unsigned char *in_buf,
|
||||
size_t in_len,
|
||||
unsigned char * out_buf,
|
||||
size_t * out_len)
|
||||
{
|
||||
return ha_algorithm_decompress(HA_ALGORITHM_HSC, in_buf, in_len, out_buf, out_len);
|
||||
}
|
||||
568
ha/hsc.c
Normal file
568
ha/hsc.c
Normal file
@@ -0,0 +1,568 @@
|
||||
/***********************************************************************
|
||||
This file is part of HA, a general purpose file archiver.
|
||||
Copyright (C) 1995 Harri Hirvola
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
************************************************************************
|
||||
HA HSC method
|
||||
***********************************************************************/
|
||||
|
||||
/***********************************************************************
|
||||
Modified to work with memory buffers instead of files by
|
||||
Copyright (C) 2005 Natalia Portillo
|
||||
************************************************************************/
|
||||
|
||||
#include "internal.h"
|
||||
#include "acoder.h"
|
||||
#include "hsc.h"
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
static decompress_context_t *g_ctx = NULL;
|
||||
|
||||
static void hsc_cleanup(void)
|
||||
{
|
||||
if(ht != NULL) free(ht), ht = NULL;
|
||||
if(fc != NULL) free(fc), fc = NULL;
|
||||
if(fa != NULL) free(fa), fa = NULL;
|
||||
if(ft != NULL) free(ft), ft = NULL;
|
||||
if(fe != NULL) free(fe), fe = NULL;
|
||||
if(nb != NULL) free(nb), nb = NULL;
|
||||
if(hp != NULL) free(hp), hp = NULL;
|
||||
if(elp != NULL) free(elp), elp = NULL;
|
||||
if(eln != NULL) free(eln), eln = NULL;
|
||||
if(cl != NULL) free(cl), cl = NULL;
|
||||
if(cc != NULL) free(cc), cc = NULL;
|
||||
if(rfm != NULL) free(rfm), rfm = NULL;
|
||||
if(con != NULL) free(con), con = NULL;
|
||||
}
|
||||
|
||||
static int putbyte_to_buffer(unsigned char c)
|
||||
{
|
||||
if(!g_ctx || g_ctx->output.pos >= g_ctx->output.max_size)
|
||||
{
|
||||
if(g_ctx) g_ctx->output.error = 1;
|
||||
return -1;
|
||||
}
|
||||
|
||||
g_ctx->output.data[g_ctx->output.pos++] = c;
|
||||
if(g_ctx->output.pos > g_ctx->output.size) { g_ctx->output.size = g_ctx->output.pos; }
|
||||
return 0;
|
||||
}
|
||||
|
||||
static U16B make_context(unsigned char cl, S16B c);
|
||||
|
||||
static int init_model(void)
|
||||
{
|
||||
register S16B i;
|
||||
S32B z, l, h, t;
|
||||
|
||||
ht = malloc(HTLEN * sizeof(*ht));
|
||||
hp = malloc(NUMCON * sizeof(*hp));
|
||||
elp = malloc(NUMCON * sizeof(*elp));
|
||||
eln = malloc(NUMCON * sizeof(*eln));
|
||||
cl = malloc(NUMCON * sizeof(*cl));
|
||||
cc = malloc(NUMCON * sizeof(*cc));
|
||||
ft = malloc(NUMCON * sizeof(*ft));
|
||||
fe = malloc(NUMCON * sizeof(*fe));
|
||||
rfm = malloc(NUMCON * sizeof(*rfm));
|
||||
con = malloc(NUMCON * sizeof(*con));
|
||||
fc = malloc(NUMCFB * sizeof(*fc));
|
||||
fa = malloc(NUMCFB * sizeof(*fa));
|
||||
nb = malloc(NUMCFB * sizeof(*nb));
|
||||
|
||||
if(hp == NULL || elp == NULL || eln == NULL || cl == NULL || rfm == NULL || con == NULL || cc == NULL || ft == NULL
|
||||
|| fe == NULL || fc == NULL || fa == NULL || nb == NULL || ht == NULL)
|
||||
{
|
||||
hsc_cleanup();
|
||||
return -1;
|
||||
}
|
||||
|
||||
maxclen = MAXCLEN;
|
||||
iec[0] = (IECLIM >> 1);
|
||||
for(i = 1; i <= MAXCLEN; ++i) iec[i] = (IECLIM >> 1) - 1;
|
||||
dropcnt = NUMCON / 4;
|
||||
nec = 0;
|
||||
nrel = 0;
|
||||
hs[0] = 0;
|
||||
|
||||
for(i = 0; i < HTLEN; ++i) ht[i] = NIL;
|
||||
for(i = 0; i < NUMCON; ++i)
|
||||
{
|
||||
eln[i] = i + 1;
|
||||
elp[i] = i - 1;
|
||||
cl[i] = 0xff;
|
||||
nb[i] = NIL;
|
||||
}
|
||||
elf = 0;
|
||||
ell = NUMCON - 1;
|
||||
|
||||
for(i = NUMCON; i < NUMCFB - 1; ++i) nb[i] = i + 1;
|
||||
nb[i] = NIL;
|
||||
fcfbl = NUMCON;
|
||||
|
||||
curcon[3] = curcon[2] = curcon[1] = curcon[0] = 0;
|
||||
cmsp = 0;
|
||||
for(i = 0; i < 256; ++i) cmask[i] = 0;
|
||||
|
||||
for(z = 10, i = 0; i < HTLEN; ++i)
|
||||
{
|
||||
h = z / (2147483647L / 16807L);
|
||||
l = z % (2147483647L / 16807L);
|
||||
if((t = 16807L * l - (2147483647L % 16807L) * h) > 0) z = t;
|
||||
else z = t + 2147483647L;
|
||||
hrt[i] = (U16B)z & (HTLEN - 1);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int init_unpack(decompress_context_t *ctx)
|
||||
{
|
||||
g_ctx = ctx;
|
||||
if(init_model() < 0) return -1;
|
||||
ac_init_decode(ctx);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define HASH(s,l,h) { \
|
||||
h = 0; \
|
||||
if (l) h = hrt[s[0]]; \
|
||||
if (l > 1) h = hrt[(s[1] + h) & (HTLEN - 1)]; \
|
||||
if (l > 2) h = hrt[(s[2] + h) & (HTLEN - 1)]; \
|
||||
if (l > 3) h = hrt[(s[3] + h) & (HTLEN - 1)]; \
|
||||
}
|
||||
|
||||
#define move_context(c) curcon[3] = curcon[2], curcon[2] = curcon[1], \
|
||||
curcon[1] = curcon[0], curcon[0] = c
|
||||
|
||||
static void release_cfblocks(void)
|
||||
{
|
||||
register U16B i, j, d;
|
||||
|
||||
do
|
||||
{
|
||||
do if(++nrel == NUMCON) nrel = 0; while(nb[nrel] == NIL);
|
||||
for(i = 0; i <= usp; ++i) if((cps[i] & 0x7fff) == nrel) break;
|
||||
}
|
||||
while(i <= usp);
|
||||
|
||||
for(i = nb[nrel], d = fa[nrel]; i != NIL; i = nb[i]) if(fa[i] < d) d = fa[i];
|
||||
++d;
|
||||
|
||||
if(fa[nrel] < d)
|
||||
{
|
||||
for(i = nb[nrel]; fa[i] < d && nb[i] != NIL; i = nb[i]);
|
||||
fa[nrel] = fa[i];
|
||||
fc[nrel] = fc[i];
|
||||
j = nb[i];
|
||||
nb[i] = fcfbl;
|
||||
fcfbl = nb[nrel];
|
||||
if((nb[nrel] = j) == NIL)
|
||||
{
|
||||
cc[nrel] = 0;
|
||||
fe[nrel] = (ft[nrel] = fa[nrel]) < ESCTH ? 1 : 0;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
fe[nrel] = (ft[nrel] = fa[nrel] /= d) < ESCTH ? 1 : 0;
|
||||
cc[nrel] = 0;
|
||||
|
||||
for(j = nrel, i = nb[j]; i != NIL;)
|
||||
{
|
||||
if(fa[i] < d)
|
||||
{
|
||||
nb[j] = nb[i];
|
||||
nb[i] = fcfbl;
|
||||
fcfbl = i;
|
||||
i = nb[j];
|
||||
}
|
||||
else
|
||||
{
|
||||
++cc[nrel];
|
||||
ft[nrel] += fa[i] /= d;
|
||||
if(fa[i] < ESCTH) fe[nrel]++;
|
||||
j = i;
|
||||
i = nb[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static U16B make_context(unsigned char conlen, S16B c)
|
||||
{
|
||||
register S16B i;
|
||||
register U16B nc, fp;
|
||||
|
||||
nc = ell;
|
||||
ell = elp[nc];
|
||||
elp[elf] = nc;
|
||||
eln[nc] = elf;
|
||||
elf = nc;
|
||||
|
||||
if(cl[nc] != 0xff)
|
||||
{
|
||||
if(cl[nc] == MAXCLEN && --dropcnt == 0) maxclen = MAXCLEN - 1;
|
||||
HASH(con[nc], cl[nc], i);
|
||||
if(ht[i] == nc) ht[i] = hp[nc];
|
||||
else
|
||||
{
|
||||
for(i = ht[i]; hp[i] != nc; i = hp[i]);
|
||||
hp[i] = hp[nc];
|
||||
}
|
||||
if(nb[nc] != NIL)
|
||||
{
|
||||
for(fp = nb[nc]; nb[fp] != NIL; fp = nb[fp]);
|
||||
nb[fp] = fcfbl;
|
||||
fcfbl = nb[nc];
|
||||
}
|
||||
}
|
||||
|
||||
nb[nc] = NIL;
|
||||
fe[nc] = ft[nc] = fa[nc] = 1;
|
||||
fc[nc] = c;
|
||||
rfm[nc] = RFMINI;
|
||||
cc[nc] = 0;
|
||||
cl[nc] = conlen;
|
||||
con[nc][0] = curcon[0];
|
||||
con[nc][1] = curcon[1];
|
||||
con[nc][2] = curcon[2];
|
||||
con[nc][3] = curcon[3];
|
||||
|
||||
HASH(curcon, conlen, i);
|
||||
hp[nc] = ht[i];
|
||||
ht[i] = nc;
|
||||
return nc;
|
||||
}
|
||||
|
||||
static void el_movefront(U16B cp)
|
||||
{
|
||||
if(cp == elf) return;
|
||||
if(cp == ell) ell = elp[cp];
|
||||
else
|
||||
{
|
||||
elp[eln[cp]] = elp[cp];
|
||||
eln[elp[cp]] = eln[cp];
|
||||
}
|
||||
elp[elf] = cp;
|
||||
eln[cp] = elf;
|
||||
elf = cp;
|
||||
}
|
||||
|
||||
static void add_model(S16B c)
|
||||
{
|
||||
register U16B i;
|
||||
register S16B cp;
|
||||
|
||||
while(usp != 0)
|
||||
{
|
||||
i = as[--usp];
|
||||
cp = cps[usp];
|
||||
if(cp & 0x8000)
|
||||
{
|
||||
cp &= 0x7fff;
|
||||
if(fcfbl == NIL) release_cfblocks();
|
||||
nb[i] = fcfbl;
|
||||
i = nb[i];
|
||||
fcfbl = nb[fcfbl];
|
||||
nb[i] = NIL;
|
||||
fa[i] = 1;
|
||||
fc[i] = c;
|
||||
++cc[cp];
|
||||
++fe[cp];
|
||||
}
|
||||
else if(++fa[i] == ESCTH) --fe[cp];
|
||||
|
||||
if((fa[i] << 1) < ++ft[cp] / (cc[cp] + 1)) --rfm[cp];
|
||||
else if(rfm[cp] < RFMINI) ++rfm[cp];
|
||||
|
||||
if(!rfm[cp] || ft[cp] >= MAXTVAL)
|
||||
{
|
||||
++rfm[cp];
|
||||
fe[cp] = ft[cp] = 0;
|
||||
for(i = cp; i != NIL; i = nb[i])
|
||||
{
|
||||
if(fa[i] > 1)
|
||||
{
|
||||
ft[cp] += fa[i] >>= 1;
|
||||
if(fa[i] < ESCTH) ++fe[cp];
|
||||
}
|
||||
else
|
||||
{
|
||||
++ft[cp];
|
||||
++fe[cp];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static U16B find_next(void)
|
||||
{
|
||||
register S16B i, k;
|
||||
register U16B cp;
|
||||
|
||||
for(i = cslen - 1; i >= 0; --i)
|
||||
{
|
||||
k = hs[i];
|
||||
for(cp = ht[k]; cp != NIL; cp = hp[cp])
|
||||
{
|
||||
if(cl[cp] == i)
|
||||
{
|
||||
switch(i)
|
||||
{
|
||||
case 4:
|
||||
if(curcon[3] != con[cp][3]) break;
|
||||
case 3:
|
||||
if(curcon[2] != con[cp][2]) break;
|
||||
case 2:
|
||||
if(curcon[1] != con[cp][1]) break;
|
||||
case 1:
|
||||
if(curcon[0] != con[cp][0]) break;
|
||||
case 0:
|
||||
cslen = i;
|
||||
return cp;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return NIL;
|
||||
}
|
||||
|
||||
static U16B find_longest(void)
|
||||
{
|
||||
hs[1] = hrt[curcon[0]];
|
||||
hs[2] = hrt[(curcon[1] + hs[1]) & (HTLEN - 1)];
|
||||
hs[3] = hrt[(curcon[2] + hs[2]) & (HTLEN - 1)];
|
||||
hs[4] = hrt[(curcon[3] + hs[3]) & (HTLEN - 1)];
|
||||
usp = 0;
|
||||
while(cmsp) cmask[cmstack[--cmsp]] = 0;
|
||||
cslen = MAXCLEN + 1;
|
||||
return find_next();
|
||||
}
|
||||
|
||||
static U16B adj_escape_prob(U16B esc, U16B cp)
|
||||
{
|
||||
if(ft[cp] == 1) return iec[cl[cp]] >= (IECLIM >> 1) ? 2 : 1;
|
||||
if(cc[cp] == 255) return 1;
|
||||
if(cc[cp] && ((cc[cp] + 1) << 1) >= ft[cp])
|
||||
{
|
||||
esc = (S16B)((S32B)esc * ((cc[cp] + 1) << 1) / ft[cp]);
|
||||
if(cc[cp] + 1 == ft[cp]) esc += (cc[cp] + 1) >> 1;
|
||||
}
|
||||
return esc ? esc : 1;
|
||||
}
|
||||
|
||||
static S16B decode_first(U16B cp)
|
||||
{
|
||||
register U16B c;
|
||||
register U16B tv;
|
||||
register U16B i;
|
||||
register S16B sum, tot, esc, cf;
|
||||
register unsigned char sv;
|
||||
|
||||
esc = adj_escape_prob(fe[cp], cp);
|
||||
tot = ft[cp];
|
||||
if(nec >= NECLIM)
|
||||
{
|
||||
if(tot <= NECTLIM && nec == NECMAX) sv = 2;
|
||||
else sv = 1;
|
||||
tot <<= sv;
|
||||
tv = ac_threshold_val(tot + esc) >> sv;
|
||||
for(c = cp, sum = 0;; c = nb[c])
|
||||
{
|
||||
if(c == NIL) break;
|
||||
if(sum + fa[c] <= tv) sum += fa[c];
|
||||
else
|
||||
{
|
||||
cf = fa[c] << sv;
|
||||
break;
|
||||
}
|
||||
}
|
||||
sum <<= sv;
|
||||
}
|
||||
else
|
||||
{
|
||||
tv = ac_threshold_val(tot + esc);
|
||||
for(c = cp, sum = 0;; c = nb[c])
|
||||
{
|
||||
if(c == NIL) break;
|
||||
if(sum + fa[c] <= tv) sum += fa[c];
|
||||
else
|
||||
{
|
||||
cf = fa[c];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
usp = 1;
|
||||
if(c != NIL)
|
||||
{
|
||||
ac_in(sum, sum + cf, tot + esc);
|
||||
if(ft[cp] == 1 && iec[cl[cp]]) --iec[cl[cp]];
|
||||
as[0] = c;
|
||||
cps[0] = cp;
|
||||
c = fc[c];
|
||||
if(nec < NECMAX) ++nec;
|
||||
}
|
||||
else
|
||||
{
|
||||
ac_in(tot, tot + esc, tot + esc);
|
||||
if(ft[cp] == 1 && iec[cl[cp]] < IECLIM) ++iec[cl[cp]];
|
||||
for(i = cp; i != NIL; sum = i, i = nb[i])
|
||||
{
|
||||
cmstack[cmsp++] = fc[i];
|
||||
cmask[fc[i]] = 1;
|
||||
}
|
||||
cps[0] = 0x8000 | cp;
|
||||
as[0] = sum;
|
||||
c = ESC;
|
||||
nec = 0;
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
static S16B decode_rest(U16B cp)
|
||||
{
|
||||
register U16B c;
|
||||
register U16B tv;
|
||||
register U16B i;
|
||||
register S16B sum, tot, esc, cf;
|
||||
|
||||
esc = tot = 0;
|
||||
for(i = cp; i != NIL; i = nb[i])
|
||||
{
|
||||
if(!cmask[fc[i]])
|
||||
{
|
||||
tot += fa[i];
|
||||
if(fa[i] < ESCTH) ++esc;
|
||||
}
|
||||
}
|
||||
esc = adj_escape_prob(esc, cp);
|
||||
tv = ac_threshold_val(tot + esc);
|
||||
|
||||
for(c = cp, sum = 0;; c = nb[c])
|
||||
{
|
||||
if(c == NIL) break;
|
||||
if(!cmask[fc[c]])
|
||||
{
|
||||
if(sum + fa[c] <= tv) sum += fa[c];
|
||||
else
|
||||
{
|
||||
cf = fa[c];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(c != NIL)
|
||||
{
|
||||
ac_in(sum, sum + cf, tot + esc);
|
||||
if(ft[cp] == 1 && iec[cl[cp]]) --iec[cl[cp]];
|
||||
as[usp] = c;
|
||||
cps[usp++] = cp;
|
||||
c = fc[c];
|
||||
++nec;
|
||||
}
|
||||
else
|
||||
{
|
||||
ac_in(tot, tot + esc, tot + esc);
|
||||
if(ft[cp] == 1 && iec[cl[cp]] < IECLIM) ++iec[cl[cp]];
|
||||
for(i = cp; i != NIL; sum = i, i = nb[i])
|
||||
{
|
||||
if(!cmask[fc[i]])
|
||||
{
|
||||
cmstack[cmsp++] = fc[i];
|
||||
cmask[fc[i]] = 1;
|
||||
}
|
||||
}
|
||||
cps[usp] = 0x8000 | cp;
|
||||
as[usp++] = sum;
|
||||
c = ESC;
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
static S16B decode_new(void)
|
||||
{
|
||||
register S16B c;
|
||||
register U16B tv, sum, tot;
|
||||
|
||||
tot = 257 - cmsp;
|
||||
tv = ac_threshold_val(tot);
|
||||
for(c = sum = 0; c < 256; ++c)
|
||||
{
|
||||
if(cmask[c]) continue;
|
||||
if(sum + 1 <= tv) ++sum;
|
||||
else break;
|
||||
}
|
||||
ac_in(sum, sum + 1, tot);
|
||||
return c;
|
||||
}
|
||||
|
||||
#define decode_byte(cp) (cmsp ? decode_rest(cp) : decode_first(cp))
|
||||
|
||||
static void flush_output(void)
|
||||
{
|
||||
/* Output flushing is handled by the buffer management */
|
||||
}
|
||||
|
||||
int hsc_unpack(decompress_context_t *ctx)
|
||||
{
|
||||
S16B c;
|
||||
U16B cp;
|
||||
unsigned char ncmax, ncmin;
|
||||
|
||||
if(init_unpack(ctx) < 0) return -1;
|
||||
|
||||
for(;;)
|
||||
{
|
||||
if(ctx->input.error || ctx->output.error) break;
|
||||
|
||||
cp = find_longest();
|
||||
ncmin = cp == NIL ? 0 : cl[cp] + 1;
|
||||
ncmax = maxclen + 1;
|
||||
|
||||
for(;;)
|
||||
{
|
||||
if(cp == NIL)
|
||||
{
|
||||
c = decode_new();
|
||||
break;
|
||||
}
|
||||
if((c = decode_byte(cp)) != ESC)
|
||||
{
|
||||
el_movefront(cp);
|
||||
break;
|
||||
}
|
||||
cp = find_next();
|
||||
}
|
||||
|
||||
if(c == ESC) break;
|
||||
|
||||
add_model(c);
|
||||
while(ncmax > ncmin) make_context(--ncmax, c);
|
||||
|
||||
if(putbyte_to_buffer(c) < 0) break;
|
||||
move_context(c);
|
||||
|
||||
if(ctx->input.error || ctx->output.error) break;
|
||||
}
|
||||
|
||||
flush_output();
|
||||
hsc_cleanup();
|
||||
return (ctx->input.error || ctx->output.error) ? -1 : 0;
|
||||
}
|
||||
90
ha/hsc.h
Normal file
90
ha/hsc.h
Normal file
@@ -0,0 +1,90 @@
|
||||
/***********************************************************************
|
||||
This file is part of HA, a general purpose file archiver.
|
||||
Copyright (C) 1995 Harri Hirvola
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
************************************************************************
|
||||
HA HSC method
|
||||
***********************************************************************/
|
||||
|
||||
/***********************************************************************
|
||||
Modified to work with memory buffers instead of files by
|
||||
Copyright (C) 2005 Natalia Portillo
|
||||
************************************************************************/
|
||||
|
||||
#ifndef HSC_H
|
||||
#define HSC_H
|
||||
|
||||
#include "internal.h"
|
||||
|
||||
int hsc_unpack(decompress_context_t *ctx);
|
||||
|
||||
#define IECLIM 32
|
||||
#define NECLIM 5
|
||||
#define NECTLIM 4
|
||||
#define NECMAX 10
|
||||
#define MAXCLEN 4
|
||||
#define NUMCON 10000
|
||||
#define NUMCFB 32760
|
||||
#define ESCTH 3
|
||||
#define MAXTVAL 8000
|
||||
#define RFMINI 4
|
||||
#define HTLEN 16384
|
||||
#define NIL 0xffff
|
||||
#define ESC 256
|
||||
|
||||
typedef unsigned char Context[4];
|
||||
|
||||
/* Model data */
|
||||
static Context curcon;
|
||||
static U16B * ht = NULL;
|
||||
static U16B * hp = NULL;
|
||||
static Context * con = NULL;
|
||||
static unsigned char *cl = NULL;
|
||||
static unsigned char *cc = NULL;
|
||||
static U16B * ft = NULL;
|
||||
static unsigned char *fe = NULL;
|
||||
static U16B * elp = NULL;
|
||||
static U16B * eln = NULL;
|
||||
static U16B elf, ell;
|
||||
static unsigned char *rfm = NULL;
|
||||
static U16B * fa = NULL;
|
||||
static unsigned char *fc = NULL;
|
||||
static U16B * nb = NULL;
|
||||
static U16B fcfbl;
|
||||
static U16B nrel;
|
||||
|
||||
/* Frequency mask system */
|
||||
static unsigned char cmask[256];
|
||||
static unsigned char cmstack[256];
|
||||
static S16B cmsp;
|
||||
|
||||
/* Escape probability modifying system variables */
|
||||
static unsigned char nec;
|
||||
static unsigned char iec[MAXCLEN + 1];
|
||||
|
||||
/* Update stack variables */
|
||||
static U16B usp;
|
||||
static U16B cps[MAXCLEN + 1];
|
||||
static U16B as[MAXCLEN + 1];
|
||||
|
||||
/* Miscellaneous */
|
||||
static S16B dropcnt;
|
||||
static unsigned char maxclen;
|
||||
static U16B hrt[HTLEN];
|
||||
static U16B hs[MAXCLEN + 1];
|
||||
static S16B cslen;
|
||||
|
||||
#endif /* HSC_H */
|
||||
45
ha/internal.h
Normal file
45
ha/internal.h
Normal file
@@ -0,0 +1,45 @@
|
||||
|
||||
/***********************************************************************
|
||||
Modified to work with memory buffers instead of files by
|
||||
Copyright (C) 2005 Natalia Portillo
|
||||
************************************************************************/
|
||||
|
||||
#ifndef INTERNAL_H
|
||||
#define INTERNAL_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
/* Type definitions matching original HA code */
|
||||
typedef int16_t S16B;
|
||||
typedef uint16_t U16B;
|
||||
typedef int32_t S32B;
|
||||
typedef uint32_t U32B;
|
||||
|
||||
/* Memory buffer management */
|
||||
typedef struct
|
||||
{
|
||||
const unsigned char *data;
|
||||
size_t size;
|
||||
size_t pos;
|
||||
int error;
|
||||
} input_buffer_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
unsigned char *data;
|
||||
size_t size;
|
||||
size_t pos;
|
||||
size_t max_size;
|
||||
int error;
|
||||
} output_buffer_t;
|
||||
|
||||
/* Decompression context */
|
||||
typedef struct
|
||||
{
|
||||
input_buffer_t input;
|
||||
output_buffer_t output;
|
||||
int algorithm;
|
||||
} decompress_context_t;
|
||||
|
||||
#endif /* INTERNAL_H */
|
||||
99
ha/swdict.c
Normal file
99
ha/swdict.c
Normal file
@@ -0,0 +1,99 @@
|
||||
/***********************************************************************
|
||||
This file is part of HA, a general purpose file archiver.
|
||||
Copyright (C) 1995 Harri Hirvola
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
************************************************************************
|
||||
HA sliding window dictionary
|
||||
***********************************************************************/
|
||||
|
||||
/***********************************************************************
|
||||
Modified to work with memory buffers instead of files by
|
||||
Copyright (C) 2005 Natalia Portillo
|
||||
************************************************************************/
|
||||
|
||||
#include "internal.h"
|
||||
#include "swdict.h"
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
U16B swd_bpos, swd_mlf;
|
||||
S16B swd_char;
|
||||
|
||||
static decompress_context_t *g_ctx = NULL;
|
||||
static U16B cblen, bbf;
|
||||
static unsigned char * b = NULL;
|
||||
|
||||
void swd_cleanup(void)
|
||||
{
|
||||
if(b != NULL)
|
||||
{
|
||||
free(b);
|
||||
b = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void swd_dinit(decompress_context_t *ctx, U16B bufl)
|
||||
{
|
||||
g_ctx = ctx;
|
||||
cblen = bufl;
|
||||
|
||||
b = malloc(cblen * sizeof(unsigned char));
|
||||
if(b == NULL)
|
||||
{
|
||||
ctx->output.error = 1;
|
||||
return;
|
||||
}
|
||||
|
||||
bbf = 0;
|
||||
}
|
||||
|
||||
static int putbyte_to_buffer(unsigned char c)
|
||||
{
|
||||
if(!g_ctx || g_ctx->output.pos >= g_ctx->output.max_size)
|
||||
{
|
||||
if(g_ctx) g_ctx->output.error = 1;
|
||||
return -1;
|
||||
}
|
||||
|
||||
g_ctx->output.data[g_ctx->output.pos++] = c;
|
||||
if(g_ctx->output.pos > g_ctx->output.size) { g_ctx->output.size = g_ctx->output.pos; }
|
||||
return 0;
|
||||
}
|
||||
|
||||
void swd_dpair(U16B l, U16B p)
|
||||
{
|
||||
if(!g_ctx || g_ctx->output.error) return;
|
||||
|
||||
if(bbf > p) p = bbf - 1 - p;
|
||||
else p = cblen - 1 - p + bbf;
|
||||
|
||||
while(l--)
|
||||
{
|
||||
b[bbf] = b[p];
|
||||
if(putbyte_to_buffer(b[p]) < 0) return;
|
||||
if(++bbf == cblen) bbf = 0;
|
||||
if(++p == cblen) p = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void swd_dchar(S16B c)
|
||||
{
|
||||
if(!g_ctx || g_ctx->output.error) return;
|
||||
|
||||
b[bbf] = c;
|
||||
if(putbyte_to_buffer(c) < 0) return;
|
||||
if(++bbf == cblen) bbf = 0;
|
||||
}
|
||||
45
ha/swdict.h
Normal file
45
ha/swdict.h
Normal file
@@ -0,0 +1,45 @@
|
||||
/***********************************************************************
|
||||
This file is part of HA, a general purpose file archiver.
|
||||
Copyright (C) 1995 Harri Hirvola
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
************************************************************************
|
||||
HA sliding window dictionary
|
||||
***********************************************************************/
|
||||
|
||||
/***********************************************************************
|
||||
Modified to work with memory buffers instead of files by
|
||||
Copyright (C) 2005 Natalia Portillo
|
||||
************************************************************************/
|
||||
|
||||
#ifndef SWDICT_H
|
||||
#define SWDICT_H
|
||||
|
||||
#include "internal.h"
|
||||
|
||||
#define MINLEN 3 /* Minimum possible match length */
|
||||
|
||||
void swd_dinit(decompress_context_t *ctx, U16B bufl);
|
||||
|
||||
void swd_cleanup(void);
|
||||
|
||||
void swd_dpair(U16B l, U16B p);
|
||||
|
||||
void swd_dchar(S16B c);
|
||||
|
||||
extern U16B swd_bpos, swd_mlf;
|
||||
extern S16B swd_char;
|
||||
|
||||
#endif /* SWDICT_H */
|
||||
30
library.h
30
library.h
@@ -124,22 +124,42 @@ AARU_EXPORT int AARU_CALL arc_decompress_pack(const unsigned char *in_buf, size_
|
||||
// ARC method 4: Huffman squeezing
|
||||
AARU_EXPORT int AARU_CALL arc_decompress_squeeze(const unsigned char *in_buf, size_t in_len, unsigned char *out_buf,
|
||||
size_t *out_len);
|
||||
// Method 5: LZW (crunching)
|
||||
// ARC Method 5: LZW (crunching)
|
||||
AARU_EXPORT int AARU_CALL arc_decompress_crunch(const unsigned char *in_buf, size_t in_len, unsigned char *out_buf,
|
||||
size_t *out_len);
|
||||
// Method 6: LZW with non-repeat packing (crunching)
|
||||
// ARC Method 6: LZW with non-repeat packing (crunching)
|
||||
AARU_EXPORT int AARU_CALL arc_decompress_crunch_nrpack(const unsigned char *in_buf, size_t in_len,
|
||||
unsigned char *out_buf, size_t *out_len);
|
||||
// Method 7: LZW with non-repeat packing and new hash (Crunching)
|
||||
// ARC Method 7: LZW with non-repeat packing and new hash (Crunching)
|
||||
AARU_EXPORT int AARU_CALL arc_decompress_crunch_nrpack_new(const unsigned char *in_buf, size_t in_len,
|
||||
unsigned char *out_buf, size_t *out_len);
|
||||
|
||||
// Method 8: Dynamic LZW (crunching)
|
||||
// ARC Method 8: Dynamic LZW (crunching)
|
||||
AARU_EXPORT int AARU_CALL arc_decompress_crunch_dynamic(const unsigned char *in_buf, size_t in_len,
|
||||
unsigned char *out_buf, size_t *out_len);
|
||||
|
||||
// Method 9: Dynamic LZW with 13 bits (squashing)
|
||||
// ARC Method 9: Dynamic LZW with 13 bits (squashing)
|
||||
AARU_EXPORT int AARU_CALL arc_decompress_squash(const unsigned char *in_buf, size_t in_len, unsigned char *out_buf,
|
||||
size_t *out_len);
|
||||
|
||||
// ARC/PAK Method 10: LZW (crush) (unsure why it's different of the others but even XADMaster uses different codepaths)
|
||||
AARU_EXPORT int AARU_CALL pak_decompress_crush(const unsigned char *in_buf, size_t in_len, unsigned char *out_buf,
|
||||
size_t *out_len);
|
||||
|
||||
// ARC/PAK Method 11: LZSS (distill)
|
||||
AARU_EXPORT int AARU_CALL pak_decompress_distill(const unsigned char *in_buf, size_t in_len, unsigned char *out_buf,
|
||||
size_t *out_len);
|
||||
|
||||
/**
|
||||
* HA Algorithm Types
|
||||
*/
|
||||
typedef enum {
|
||||
HA_ALGORITHM_ASC = 0, /* ASC algorithm */
|
||||
HA_ALGORITHM_HSC = 1 /* HSC algorithm */
|
||||
} ha_algorithm_t;
|
||||
|
||||
AARU_EXPORT int AARU_CALL ha_asc_decompress(const unsigned char *in_buf, size_t in_len, unsigned char *out_buf, size_t *out_len);
|
||||
|
||||
AARU_EXPORT int AARU_CALL ha_hsc_decompress(const unsigned char *in_buf, size_t in_len, unsigned char *out_buf, size_t *out_len);
|
||||
|
||||
#endif // AARU_COMPRESSION_NATIVE_LIBRARY_H
|
||||
|
||||
166
pak/bitstream.c
Normal file
166
pak/bitstream.c
Normal file
@@ -0,0 +1,166 @@
|
||||
/*
|
||||
* bitstream.c - Bit stream input implementation
|
||||
*
|
||||
* Copyright (c) 2017-present, MacPaw Inc. All rights reserved.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||
* MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include "bitstream.h"
|
||||
|
||||
void bitstream_init(BitStream *bs, const uint8_t *data, size_t length)
|
||||
{
|
||||
bs->data = data;
|
||||
bs->length = length;
|
||||
bs->pos = 0;
|
||||
bs->bitbuffer = 0;
|
||||
bs->bitcount = 0;
|
||||
bs->eof = false;
|
||||
}
|
||||
|
||||
static void bitstream_fill_buffer(BitStream *bs)
|
||||
{
|
||||
while(bs->bitcount < 24 && bs->pos < bs->length)
|
||||
{
|
||||
bs->bitbuffer |= (uint32_t)bs->data[bs->pos] << (24 - bs->bitcount);
|
||||
bs->bitcount += 8;
|
||||
bs->pos++;
|
||||
}
|
||||
if(bs->pos >= bs->length && bs->bitcount == 0) { bs->eof = true; }
|
||||
}
|
||||
|
||||
uint32_t bitstream_read_bit(BitStream *bs)
|
||||
{
|
||||
if(bs->eof) return 0;
|
||||
|
||||
if(bs->bitcount == 0) { bitstream_fill_buffer(bs); }
|
||||
|
||||
if(bs->bitcount == 0)
|
||||
{
|
||||
bs->eof = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t bit = (bs->bitbuffer >> 31) & 1;
|
||||
bs->bitbuffer <<= 1;
|
||||
bs->bitcount--;
|
||||
|
||||
return bit;
|
||||
}
|
||||
|
||||
uint32_t bitstream_read_bit_le(BitStream *bs)
|
||||
{
|
||||
if(bs->eof) return 0;
|
||||
|
||||
if(bs->bitcount == 0)
|
||||
{
|
||||
if(bs->pos >= bs->length)
|
||||
{
|
||||
bs->eof = true;
|
||||
return 0;
|
||||
}
|
||||
bs->bitbuffer = bs->data[bs->pos++];
|
||||
bs->bitcount = 8;
|
||||
}
|
||||
|
||||
uint32_t bit = bs->bitbuffer & 1;
|
||||
bs->bitbuffer >>= 1;
|
||||
bs->bitcount--;
|
||||
|
||||
return bit;
|
||||
}
|
||||
|
||||
uint32_t bitstream_read_bits(BitStream *bs, int count)
|
||||
{
|
||||
uint32_t result = 0;
|
||||
for(int i = 0; i < count; i++) { result = (result << 1) | bitstream_read_bit(bs); }
|
||||
return result;
|
||||
}
|
||||
|
||||
uint32_t bitstream_read_bits_le(BitStream *bs, int count)
|
||||
{
|
||||
uint32_t result = 0;
|
||||
for(int i = 0; i < count; i++) { result |= bitstream_read_bit_le(bs) << i; }
|
||||
return result;
|
||||
}
|
||||
|
||||
uint32_t bitstream_peek_bits(BitStream *bs, int count)
|
||||
{
|
||||
// Save current state
|
||||
uint32_t saved_buffer = bs->bitbuffer;
|
||||
int saved_bitcount = bs->bitcount;
|
||||
size_t saved_pos = bs->pos;
|
||||
bool saved_eof = bs->eof;
|
||||
|
||||
// Read the bits
|
||||
uint32_t result = bitstream_read_bits(bs, count);
|
||||
|
||||
// Restore state
|
||||
bs->bitbuffer = saved_buffer;
|
||||
bs->bitcount = saved_bitcount;
|
||||
bs->pos = saved_pos;
|
||||
bs->eof = saved_eof;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
uint32_t bitstream_peek_bits_le(BitStream *bs, int count)
|
||||
{
|
||||
// Save current state
|
||||
uint32_t saved_buffer = bs->bitbuffer;
|
||||
int saved_bitcount = bs->bitcount;
|
||||
size_t saved_pos = bs->pos;
|
||||
bool saved_eof = bs->eof;
|
||||
|
||||
// Read the bits
|
||||
uint32_t result = bitstream_read_bits_le(bs, count);
|
||||
|
||||
// Restore state
|
||||
bs->bitbuffer = saved_buffer;
|
||||
bs->bitcount = saved_bitcount;
|
||||
bs->pos = saved_pos;
|
||||
bs->eof = saved_eof;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void bitstream_skip_bits(BitStream *bs, int count) { bitstream_read_bits(bs, count); }
|
||||
|
||||
void bitstream_skip_bits_le(BitStream *bs, int count) { bitstream_read_bits_le(bs, count); }
|
||||
|
||||
uint8_t bitstream_read_byte(BitStream *bs)
|
||||
{
|
||||
if(bs->pos >= bs->length)
|
||||
{
|
||||
bs->eof = true;
|
||||
return 0;
|
||||
}
|
||||
return bs->data[bs->pos++];
|
||||
}
|
||||
|
||||
uint16_t bitstream_read_uint16_le(BitStream *bs)
|
||||
{
|
||||
if(bs->pos + 1 >= bs->length)
|
||||
{
|
||||
bs->eof = true;
|
||||
return 0;
|
||||
}
|
||||
uint16_t result = bs->data[bs->pos] | (bs->data[bs->pos + 1] << 8);
|
||||
bs->pos += 2;
|
||||
return result;
|
||||
}
|
||||
|
||||
bool bitstream_eof(BitStream *bs) { return bs->eof; }
|
||||
75
pak/bitstream.h
Normal file
75
pak/bitstream.h
Normal file
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
* bitstream.h - Bit stream input implementation
|
||||
*
|
||||
* Copyright (c) 2017-present, MacPaw Inc. All rights reserved.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||
* MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#ifndef BITSTREAM_H
|
||||
#define BITSTREAM_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
typedef struct BitStream
|
||||
{
|
||||
const uint8_t *data;
|
||||
size_t length;
|
||||
size_t pos;
|
||||
uint32_t bitbuffer;
|
||||
int bitcount;
|
||||
bool eof;
|
||||
} BitStream;
|
||||
|
||||
// Initialize bit stream
|
||||
void bitstream_init(BitStream *bs, const uint8_t *data, size_t length);
|
||||
|
||||
// Read a single bit (MSB first)
|
||||
uint32_t bitstream_read_bit(BitStream *bs);
|
||||
|
||||
// Read a single bit (LSB first)
|
||||
uint32_t bitstream_read_bit_le(BitStream *bs);
|
||||
|
||||
// Read multiple bits (MSB first)
|
||||
uint32_t bitstream_read_bits(BitStream *bs, int count);
|
||||
|
||||
// Read multiple bits (LSB first)
|
||||
uint32_t bitstream_read_bits_le(BitStream *bs, int count);
|
||||
|
||||
// Peek at bits without consuming them (MSB first)
|
||||
uint32_t bitstream_peek_bits(BitStream *bs, int count);
|
||||
|
||||
// Peek at bits without consuming them (LSB first)
|
||||
uint32_t bitstream_peek_bits_le(BitStream *bs, int count);
|
||||
|
||||
// Skip previously peeked bits (MSB first)
|
||||
void bitstream_skip_bits(BitStream *bs, int count);
|
||||
|
||||
// Skip previously peeked bits (LSB first)
|
||||
void bitstream_skip_bits_le(BitStream *bs, int count);
|
||||
|
||||
// Read a byte
|
||||
uint8_t bitstream_read_byte(BitStream *bs);
|
||||
|
||||
// Read a 16-bit little endian integer
|
||||
uint16_t bitstream_read_uint16_le(BitStream *bs);
|
||||
|
||||
// Check if end of stream reached
|
||||
bool bitstream_eof(BitStream *bs);
|
||||
|
||||
#endif /* BITSTREAM_H */
|
||||
219
pak/crush.c
Normal file
219
pak/crush.c
Normal file
@@ -0,0 +1,219 @@
|
||||
/*
|
||||
* arc_crush.c - ARC Crush decompression algorithm
|
||||
*
|
||||
* Copyright (c) 2017-present, MacPaw Inc. All rights reserved.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||
* MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include <limits.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
#include "../library.h"
|
||||
#include "bitstream.h"
|
||||
#include "lzw.h"
|
||||
|
||||
int pak_decompress_crush_internal(const unsigned char *in_buf, size_t in_len, unsigned char *out_buf, size_t *out_len)
|
||||
{
|
||||
if(!in_buf || !out_buf || !out_len || in_len == 0) { return -1; }
|
||||
|
||||
BitStream bs;
|
||||
bitstream_init(&bs, (const uint8_t *)in_buf, in_len);
|
||||
|
||||
LZW *lzw = lzw_alloc(8192, 1);
|
||||
if(!lzw) { return -1; }
|
||||
|
||||
// Initialize state
|
||||
int symbolsize = 1;
|
||||
int nextsizebump = 2;
|
||||
bool useliteralbit = true;
|
||||
|
||||
int numrecentstrings = 0;
|
||||
int ringindex = 0;
|
||||
bool stringring[500];
|
||||
memset(stringring, 0, sizeof(stringring));
|
||||
|
||||
int usageindex = 0x101;
|
||||
uint8_t usage[8192];
|
||||
memset(usage, 0, sizeof(usage));
|
||||
|
||||
int currbyte = 0;
|
||||
uint8_t buffer[8192];
|
||||
size_t outpos = 0;
|
||||
size_t max_output = *out_len;
|
||||
|
||||
while(!bitstream_eof(&bs) && outpos < max_output)
|
||||
{
|
||||
if(!currbyte)
|
||||
{
|
||||
// Read the next symbol. How depends on the mode we are operating in.
|
||||
int symbol;
|
||||
if(useliteralbit)
|
||||
{
|
||||
// Use codes prefixed by a bit that selects literal or string codes.
|
||||
// Literals are always 8 bits, strings vary.
|
||||
if(bitstream_read_bit_le(&bs)) { symbol = bitstream_read_bits_le(&bs, symbolsize) + 256; }
|
||||
else { symbol = bitstream_read_bits_le(&bs, 8); }
|
||||
}
|
||||
else
|
||||
{
|
||||
// Use same-length codes for both literals and strings.
|
||||
// Due to an optimization quirk in the original decruncher,
|
||||
// literals have their bits inverted.
|
||||
symbol = bitstream_read_bits_le(&bs, symbolsize);
|
||||
if(symbol < 0x100) symbol ^= 0xff;
|
||||
}
|
||||
|
||||
// Code 0x100 is the EOF code.
|
||||
if(symbol == 0x100) { break; }
|
||||
|
||||
// Walk through the LZW tree, and set the usage count of the current
|
||||
// string and all its parents to 4. This is not necessary for literals,
|
||||
// but we do it anyway for simplicity.
|
||||
LZWTreeNode *nodes = lzw_symbols(lzw);
|
||||
int marksymbol = symbol;
|
||||
while(marksymbol >= 0)
|
||||
{
|
||||
if(marksymbol < 8192) { usage[marksymbol] = 4; }
|
||||
marksymbol = nodes[marksymbol].parent;
|
||||
}
|
||||
|
||||
// Adjust the count of recent strings versus literals.
|
||||
// Use a ring buffer of length 500 as a window to keep track
|
||||
// of how many strings have been encountered lately.
|
||||
|
||||
// First, decrease the count if a string leaves the window.
|
||||
if(stringring[ringindex]) numrecentstrings--;
|
||||
|
||||
// Then store the current type of symbol in the window, and
|
||||
// increase the count if the current symbol is a string.
|
||||
if(symbol < 0x100) { stringring[ringindex] = false; }
|
||||
else
|
||||
{
|
||||
stringring[ringindex] = true;
|
||||
numrecentstrings++;
|
||||
}
|
||||
|
||||
// Move the window forward.
|
||||
ringindex = (ringindex + 1) % 500;
|
||||
|
||||
// Check the number of strings. If there have been many literals
|
||||
// lately, bit-prefixed codes should be used. If we need to change
|
||||
// mode, re-calculate the point where we increase the code length.
|
||||
bool manyliterals = numrecentstrings < 375;
|
||||
if(manyliterals != useliteralbit)
|
||||
{
|
||||
useliteralbit = manyliterals;
|
||||
nextsizebump = 1 << symbolsize;
|
||||
if(!useliteralbit) nextsizebump -= 0x100;
|
||||
}
|
||||
|
||||
// Update the LZW tree.
|
||||
if(!lzw_symbol_list_full(lzw))
|
||||
{
|
||||
// If there is space in the tree, just add a new string as usual.
|
||||
if(lzw_next_symbol(lzw, symbol) != LZW_NO_ERROR)
|
||||
{
|
||||
lzw_free(lzw);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Set the usage count of the newly created entry.
|
||||
int count = lzw_symbol_count(lzw);
|
||||
if(count > 0 && count - 1 < 8192) { usage[count - 1] = 2; }
|
||||
}
|
||||
else
|
||||
{
|
||||
// If the tree is full, find a less-used symbol, and replace it.
|
||||
int minindex = 0, minusage = INT_MAX;
|
||||
int index = usageindex;
|
||||
do {
|
||||
index++;
|
||||
if(index == 8192) index = 0x101;
|
||||
|
||||
if(usage[index] < minusage)
|
||||
{
|
||||
minindex = index;
|
||||
minusage = usage[index];
|
||||
}
|
||||
|
||||
usage[index]--;
|
||||
if(usage[index] == 0) break;
|
||||
} while(index != usageindex);
|
||||
|
||||
usageindex = index;
|
||||
|
||||
if(lzw_replace_symbol(lzw, minindex, symbol) != LZW_NO_ERROR)
|
||||
{
|
||||
lzw_free(lzw);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Set the usage count of the replaced entry.
|
||||
if(minindex < 8192) { usage[minindex] = 2; }
|
||||
}
|
||||
|
||||
// Extract the data to output.
|
||||
currbyte = lzw_reverse_output_to_buffer(lzw, buffer);
|
||||
|
||||
// Check if we need to increase the code size. The point at which
|
||||
// to increase varies depending on the coding mode.
|
||||
if(lzw_symbol_count(lzw) - 257 >= nextsizebump)
|
||||
{
|
||||
symbolsize++;
|
||||
nextsizebump = 1 << symbolsize;
|
||||
if(!useliteralbit) nextsizebump -= 0x100;
|
||||
}
|
||||
}
|
||||
|
||||
if(currbyte > 0 && outpos < max_output) { out_buf[outpos++] = (char)buffer[--currbyte]; }
|
||||
else if(currbyte == 0)
|
||||
{
|
||||
// No more bytes in buffer, continue to next symbol
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Output buffer full
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
lzw_free(lzw);
|
||||
*out_len = outpos;
|
||||
return 0;
|
||||
}
|
||||
|
||||
AARU_EXPORT int AARU_CALL pak_decompress_crush(const unsigned char *in_buf, size_t in_len, unsigned char *out_buf,
|
||||
size_t *out_len)
|
||||
{
|
||||
// Allocate a temporary buffer.
|
||||
size_t temp_len = *out_len * 2; // Heuristic.
|
||||
unsigned char *temp_buf = malloc(temp_len);
|
||||
if(!temp_buf) return -1;
|
||||
|
||||
// Decompress crunched data.
|
||||
int result = pak_decompress_crush_internal(in_buf, in_len, temp_buf, &temp_len);
|
||||
if(result == 0)
|
||||
{
|
||||
// Decompress non-repeat packing.
|
||||
result = arc_decompress_pack(temp_buf, temp_len, out_buf, out_len);
|
||||
}
|
||||
|
||||
free(temp_buf);
|
||||
return result;
|
||||
}
|
||||
182
pak/distill.c
Normal file
182
pak/distill.c
Normal file
@@ -0,0 +1,182 @@
|
||||
/*
|
||||
* arc_distill.c - ARC Distill decompression algorithm
|
||||
*
|
||||
* Copyright (c) 2017-present, MacPaw Inc. All rights reserved.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||
* MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "../library.h"
|
||||
#include "bitstream.h"
|
||||
#include "prefixcode.h"
|
||||
|
||||
static const int offset_lengths[0x40] = {
|
||||
3, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7, 7, 7,
|
||||
7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
|
||||
};
|
||||
|
||||
static const int offset_codes[0x40] = {
|
||||
0x00, 0x02, 0x04, 0x0c, 0x01, 0x06, 0x0a, 0x0e, 0x11, 0x16, 0x1a, 0x1e, 0x05, 0x09, 0x0d, 0x15,
|
||||
0x19, 0x1d, 0x25, 0x29, 0x2d, 0x35, 0x39, 0x3d, 0x03, 0x07, 0x0b, 0x13, 0x17, 0x1b, 0x23, 0x27,
|
||||
0x2b, 0x33, 0x37, 0x3b, 0x43, 0x47, 0x4b, 0x53, 0x57, 0x5b, 0x63, 0x67, 0x6b, 0x73, 0x77, 0x7b,
|
||||
0x0f, 0x1f, 0x2f, 0x3f, 0x4f, 0x5f, 0x6f, 0x7f, 0x8f, 0x9f, 0xaf, 0xbf, 0xcf, 0xdf, 0xef, 0xff,
|
||||
};
|
||||
|
||||
static void build_code_from_tree(PrefixCode *code, int *tree, int node, int numnodes, int depth)
|
||||
{
|
||||
if(depth > 64)
|
||||
{
|
||||
// Too deep - error
|
||||
return;
|
||||
}
|
||||
|
||||
if(node >= numnodes) { prefix_code_make_leaf_with_value(code, node - numnodes); }
|
||||
else
|
||||
{
|
||||
prefix_code_start_zero_branch(code);
|
||||
build_code_from_tree(code, tree, tree[node], numnodes, depth + 1);
|
||||
prefix_code_start_one_branch(code);
|
||||
build_code_from_tree(code, tree, tree[node + 1], numnodes, depth + 1);
|
||||
prefix_code_finish_branches(code);
|
||||
}
|
||||
}
|
||||
|
||||
AARU_EXPORT int AARU_CALL pak_decompress_distill(const unsigned char *in_buf, size_t in_len, unsigned char *out_buf,
|
||||
size_t *out_len)
|
||||
{
|
||||
if(!in_buf || !out_buf || !out_len || in_len == 0) { return -1; }
|
||||
|
||||
BitStream bs;
|
||||
bitstream_init(&bs, (const uint8_t *)in_buf, in_len);
|
||||
|
||||
// Read header information
|
||||
int numnodes = bitstream_read_uint16_le(&bs);
|
||||
int codelength = bitstream_read_byte(&bs);
|
||||
|
||||
if(numnodes < 2 || numnodes > 0x274) { return -1; }
|
||||
|
||||
// Read tree nodes
|
||||
int *nodes = malloc(numnodes * sizeof(int));
|
||||
if(!nodes) { return -1; }
|
||||
|
||||
for(int i = 0; i < numnodes; i++) { nodes[i] = bitstream_read_bits_le(&bs, codelength); }
|
||||
|
||||
// Build main code tree
|
||||
PrefixCode *maincode = prefix_code_alloc();
|
||||
if(!maincode)
|
||||
{
|
||||
free(nodes);
|
||||
return -1;
|
||||
}
|
||||
|
||||
prefix_code_start_building_tree(maincode);
|
||||
build_code_from_tree(maincode, nodes, numnodes - 2, numnodes, 0);
|
||||
|
||||
free(nodes);
|
||||
|
||||
// Build offset code tree
|
||||
PrefixCode *offsetcode = prefix_code_alloc();
|
||||
if(!offsetcode)
|
||||
{
|
||||
prefix_code_free(maincode);
|
||||
return -1;
|
||||
}
|
||||
|
||||
for(int i = 0; i < 0x40; i++)
|
||||
{
|
||||
if(prefix_code_add_value_low_bit_first(offsetcode, i, offset_codes[i], offset_lengths[i]) != PREFIX_CODE_OK)
|
||||
{
|
||||
prefix_code_free(maincode);
|
||||
prefix_code_free(offsetcode);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
// LZSS decompression
|
||||
uint8_t window[8192];
|
||||
memset(window, 0, sizeof(window));
|
||||
int windowpos = 0;
|
||||
size_t outpos = 0;
|
||||
size_t max_output = *out_len;
|
||||
|
||||
while(!bitstream_eof(&bs) && outpos < max_output)
|
||||
{
|
||||
int symbol = prefix_code_read_symbol_le(&bs, maincode);
|
||||
if(symbol < 0) break;
|
||||
|
||||
if(symbol < 256)
|
||||
{
|
||||
// Literal byte
|
||||
if(outpos < max_output) { out_buf[outpos++] = (char)symbol; }
|
||||
window[windowpos] = symbol;
|
||||
windowpos = (windowpos + 1) & 0x1fff;
|
||||
}
|
||||
else if(symbol == 256)
|
||||
{
|
||||
// End of stream
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Match
|
||||
int length = symbol - 0x101 + 3;
|
||||
int offsetsymbol = prefix_code_read_symbol_le(&bs, offsetcode);
|
||||
if(offsetsymbol < 0) break;
|
||||
|
||||
int extralength;
|
||||
if(outpos >= 0x1000 - 0x3c)
|
||||
extralength = 7;
|
||||
else if(outpos >= 0x800 - 0x3c)
|
||||
extralength = 6;
|
||||
else if(outpos >= 0x400 - 0x3c)
|
||||
extralength = 5;
|
||||
else if(outpos >= 0x200 - 0x3c)
|
||||
extralength = 4;
|
||||
else if(outpos >= 0x100 - 0x3c)
|
||||
extralength = 3;
|
||||
else if(outpos >= 0x80 - 0x3c)
|
||||
extralength = 2;
|
||||
else if(outpos >= 0x40 - 0x3c)
|
||||
extralength = 1;
|
||||
else
|
||||
extralength = 0;
|
||||
|
||||
int extrabits = bitstream_read_bits_le(&bs, extralength);
|
||||
int offset = (offsetsymbol << extralength) + extrabits + 1;
|
||||
|
||||
// Copy match
|
||||
for(int i = 0; i < length; i++)
|
||||
{
|
||||
int sourcepos = (windowpos - offset) & 0x1fff;
|
||||
uint8_t byte = window[sourcepos];
|
||||
|
||||
if(outpos < max_output) { out_buf[outpos++] = (char)byte; }
|
||||
|
||||
window[windowpos] = byte;
|
||||
windowpos = (windowpos + 1) & 0x1fff;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
prefix_code_free(maincode);
|
||||
prefix_code_free(offsetcode);
|
||||
|
||||
*out_len = outpos;
|
||||
return 0;
|
||||
}
|
||||
162
pak/lzw.c
Normal file
162
pak/lzw.c
Normal file
@@ -0,0 +1,162 @@
|
||||
/*
|
||||
* lzw.c - LZW decompression implementation
|
||||
*
|
||||
* Copyright (c) 2017-present, MacPaw Inc. All rights reserved.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||
* MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include "lzw.h"
|
||||
#include <stdlib.h>
|
||||
|
||||
LZW *lzw_alloc(int maxsymbols, int reservedsymbols)
|
||||
{
|
||||
LZW *self = (LZW *)malloc(sizeof(LZW) + sizeof(LZWTreeNode) * maxsymbols);
|
||||
if(!self) return NULL;
|
||||
|
||||
if(maxsymbols < 256 + reservedsymbols)
|
||||
{
|
||||
free(self);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
self->maxsymbols = maxsymbols;
|
||||
self->reservedsymbols = reservedsymbols;
|
||||
|
||||
self->buffer = NULL;
|
||||
self->buffersize = 0;
|
||||
|
||||
for(int i = 0; i < 256; i++)
|
||||
{
|
||||
self->nodes[i].chr = i;
|
||||
self->nodes[i].parent = -1;
|
||||
}
|
||||
|
||||
lzw_clear_table(self);
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
void lzw_free(LZW *self)
|
||||
{
|
||||
if(self)
|
||||
{
|
||||
free(self->buffer);
|
||||
free(self);
|
||||
}
|
||||
}
|
||||
|
||||
void lzw_clear_table(LZW *self)
|
||||
{
|
||||
self->numsymbols = 256 + self->reservedsymbols;
|
||||
self->prevsymbol = -1;
|
||||
self->symbolsize = 9; // TODO: technically this depends on reservedsymbols
|
||||
}
|
||||
|
||||
static uint8_t find_first_byte(LZWTreeNode *nodes, int symbol)
|
||||
{
|
||||
while(nodes[symbol].parent >= 0) symbol = nodes[symbol].parent;
|
||||
return nodes[symbol].chr;
|
||||
}
|
||||
|
||||
int lzw_next_symbol(LZW *self, int symbol)
|
||||
{
|
||||
if(self->prevsymbol < 0)
|
||||
{
|
||||
if(symbol >= self->numsymbols) return LZW_INVALID_CODE_ERROR;
|
||||
self->prevsymbol = symbol;
|
||||
return LZW_NO_ERROR;
|
||||
}
|
||||
|
||||
int postfixbyte;
|
||||
if(symbol < self->numsymbols) { postfixbyte = find_first_byte(self->nodes, symbol); }
|
||||
else if(symbol == self->numsymbols) { postfixbyte = find_first_byte(self->nodes, self->prevsymbol); }
|
||||
else { return LZW_INVALID_CODE_ERROR; }
|
||||
|
||||
int parent = self->prevsymbol;
|
||||
self->prevsymbol = symbol;
|
||||
|
||||
if(!lzw_symbol_list_full(self))
|
||||
{
|
||||
self->nodes[self->numsymbols].parent = parent;
|
||||
self->nodes[self->numsymbols].chr = postfixbyte;
|
||||
self->numsymbols++;
|
||||
|
||||
if(!lzw_symbol_list_full(self))
|
||||
{
|
||||
if((self->numsymbols & (self->numsymbols - 1)) == 0) { self->symbolsize++; }
|
||||
}
|
||||
|
||||
return LZW_NO_ERROR;
|
||||
}
|
||||
else { return LZW_TOO_MANY_CODES_ERROR; }
|
||||
}
|
||||
|
||||
int lzw_replace_symbol(LZW *self, int oldsymbol, int symbol)
|
||||
{
|
||||
if(symbol >= self->numsymbols) return LZW_INVALID_CODE_ERROR;
|
||||
|
||||
self->nodes[oldsymbol].parent = self->prevsymbol;
|
||||
self->nodes[oldsymbol].chr = find_first_byte(self->nodes, symbol);
|
||||
|
||||
self->prevsymbol = symbol;
|
||||
|
||||
return LZW_NO_ERROR;
|
||||
}
|
||||
|
||||
int lzw_output_length(LZW *self)
|
||||
{
|
||||
int symbol = self->prevsymbol;
|
||||
int n = 0;
|
||||
|
||||
while(symbol >= 0)
|
||||
{
|
||||
symbol = self->nodes[symbol].parent;
|
||||
n++;
|
||||
}
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
int lzw_output_to_buffer(LZW *self, uint8_t *buffer)
|
||||
{
|
||||
int symbol = self->prevsymbol;
|
||||
int n = lzw_output_length(self);
|
||||
buffer += n;
|
||||
|
||||
while(symbol >= 0)
|
||||
{
|
||||
*--buffer = self->nodes[symbol].chr;
|
||||
symbol = self->nodes[symbol].parent;
|
||||
}
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
int lzw_reverse_output_to_buffer(LZW *self, uint8_t *buffer)
|
||||
{
|
||||
int symbol = self->prevsymbol;
|
||||
int n = 0;
|
||||
|
||||
while(symbol >= 0)
|
||||
{
|
||||
*buffer++ = self->nodes[symbol].chr;
|
||||
symbol = self->nodes[symbol].parent;
|
||||
n++;
|
||||
}
|
||||
|
||||
return n;
|
||||
}
|
||||
83
pak/lzw.h
Normal file
83
pak/lzw.h
Normal file
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
* lzw.h - LZW decompression implementation
|
||||
*
|
||||
* Copyright (c) 2017-present, MacPaw Inc. All rights reserved.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||
* MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#ifndef LZW_H
|
||||
#define LZW_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#define LZW_NO_ERROR 0
|
||||
#define LZW_INVALID_CODE_ERROR 1
|
||||
#define LZW_TOO_MANY_CODES_ERROR 2
|
||||
|
||||
typedef struct LZWTreeNode
|
||||
{
|
||||
uint8_t chr;
|
||||
int parent;
|
||||
} LZWTreeNode;
|
||||
|
||||
typedef struct LZW
|
||||
{
|
||||
int numsymbols;
|
||||
int maxsymbols;
|
||||
int reservedsymbols;
|
||||
int prevsymbol;
|
||||
int symbolsize;
|
||||
|
||||
uint8_t *buffer;
|
||||
int buffersize;
|
||||
|
||||
LZWTreeNode nodes[]; // Flexible array member (C99)
|
||||
} LZW;
|
||||
|
||||
// Allocate LZW structure
|
||||
LZW *lzw_alloc(int maxsymbols, int reservedsymbols);
|
||||
|
||||
// Free LZW structure
|
||||
void lzw_free(LZW *self);
|
||||
|
||||
// Clear/reset LZW table
|
||||
void lzw_clear_table(LZW *self);
|
||||
|
||||
// Process next symbol
|
||||
int lzw_next_symbol(LZW *self, int symbol);
|
||||
|
||||
// Replace a symbol
|
||||
int lzw_replace_symbol(LZW *self, int oldsymbol, int symbol);
|
||||
|
||||
// Get output length
|
||||
int lzw_output_length(LZW *self);
|
||||
|
||||
// Output to buffer (normal order)
|
||||
int lzw_output_to_buffer(LZW *self, uint8_t *buffer);
|
||||
|
||||
// Output to buffer (reverse order)
|
||||
int lzw_reverse_output_to_buffer(LZW *self, uint8_t *buffer);
|
||||
|
||||
// Inline helper functions
|
||||
static inline int lzw_symbol_count(LZW *self) { return self->numsymbols; }
|
||||
|
||||
static inline bool lzw_symbol_list_full(LZW *self) { return self->numsymbols == self->maxsymbols; }
|
||||
|
||||
static inline LZWTreeNode *lzw_symbols(LZW *self) { return self->nodes; }
|
||||
|
||||
#endif /* LZW_H */
|
||||
539
pak/prefixcode.c
Normal file
539
pak/prefixcode.c
Normal file
@@ -0,0 +1,539 @@
|
||||
/*
|
||||
* prefixcode.c - Prefix code tree implementation
|
||||
*
|
||||
* Copyright (c) 2017-pstatic inline bool is_invalid_node(PrefixCode *self, int node) {
|
||||
(void)self; // Suppress unused parameter warning
|
||||
return (node < 0);
|
||||
}ent, MacPaw Inc. All rights reserved.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||
* MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include "prefixcode.h"
|
||||
#include <limits.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
// Safe realloc that frees original pointer on failure
|
||||
static void *safe_realloc(void *ptr, size_t newsize)
|
||||
{
|
||||
void *newptr = realloc(ptr, newsize);
|
||||
if(!newptr && newsize > 0)
|
||||
{
|
||||
free(ptr);
|
||||
return NULL;
|
||||
}
|
||||
return newptr;
|
||||
}
|
||||
|
||||
// Inline helper functions
|
||||
static inline CodeTreeNode *node_pointer(PrefixCode *self, int node) { return &self->tree[node]; }
|
||||
|
||||
static inline int branch(PrefixCode *self, int node, int bit) { return node_pointer(self, node)->branches[bit]; }
|
||||
|
||||
static inline void set_branch(PrefixCode *self, int node, int bit, int nextnode)
|
||||
{
|
||||
node_pointer(self, node)->branches[bit] = nextnode;
|
||||
}
|
||||
|
||||
static inline int left_branch(PrefixCode *self, int node) { return branch(self, node, 0); }
|
||||
|
||||
static inline int right_branch(PrefixCode *self, int node) { return branch(self, node, 1); }
|
||||
|
||||
static inline void set_left_branch(PrefixCode *self, int node, int nextnode) { set_branch(self, node, 0, nextnode); }
|
||||
|
||||
static inline void set_right_branch(PrefixCode *self, int node, int nextnode) { set_branch(self, node, 1, nextnode); }
|
||||
|
||||
static inline int leaf_value(PrefixCode *self, int node) { return left_branch(self, node); }
|
||||
|
||||
static inline void set_leaf_value(PrefixCode *self, int node, int value)
|
||||
{
|
||||
set_left_branch(self, node, value);
|
||||
set_right_branch(self, node, value);
|
||||
}
|
||||
|
||||
static inline void set_empty_node(PrefixCode *self, int node)
|
||||
{
|
||||
set_left_branch(self, node, -1);
|
||||
set_right_branch(self, node, -2);
|
||||
}
|
||||
|
||||
static inline bool is_invalid_node(PrefixCode *self, int node) { return node < 0; }
|
||||
|
||||
static inline bool is_open_branch(PrefixCode *self, int node, int bit)
|
||||
{
|
||||
return is_invalid_node(self, branch(self, node, bit));
|
||||
}
|
||||
|
||||
static inline bool is_empty_node(PrefixCode *self, int node)
|
||||
{
|
||||
return left_branch(self, node) == -1 && right_branch(self, node) == -2;
|
||||
}
|
||||
|
||||
static inline bool is_leaf_node(PrefixCode *self, int node)
|
||||
{
|
||||
return left_branch(self, node) == right_branch(self, node);
|
||||
}
|
||||
|
||||
static int new_node(PrefixCode *self)
|
||||
{
|
||||
CodeTreeNode *newtree = safe_realloc(self->tree, (self->numentries + 1) * sizeof(CodeTreeNode));
|
||||
if(!newtree) return -1;
|
||||
|
||||
self->tree = newtree;
|
||||
set_empty_node(self, self->numentries);
|
||||
return self->numentries++;
|
||||
}
|
||||
|
||||
// Stack implementation for tree building
|
||||
static PrefixCodeStack *prefix_code_stack_alloc(void)
|
||||
{
|
||||
PrefixCodeStack *stack = malloc(sizeof(PrefixCodeStack));
|
||||
if(!stack) return NULL;
|
||||
|
||||
stack->data = malloc(16 * sizeof(int));
|
||||
if(!stack->data)
|
||||
{
|
||||
free(stack);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
stack->count = 0;
|
||||
stack->capacity = 16;
|
||||
return stack;
|
||||
}
|
||||
|
||||
static void prefix_code_stack_free(PrefixCodeStack *stack)
|
||||
{
|
||||
if(!stack) return;
|
||||
free(stack->data);
|
||||
free(stack);
|
||||
}
|
||||
|
||||
static int prefix_code_stack_push(PrefixCodeStack *stack, int value)
|
||||
{
|
||||
if(stack->count >= stack->capacity)
|
||||
{
|
||||
int newcapacity = stack->capacity * 2;
|
||||
int *newdata = safe_realloc(stack->data, newcapacity * sizeof(int));
|
||||
if(!newdata) return -1;
|
||||
|
||||
stack->data = newdata;
|
||||
stack->capacity = newcapacity;
|
||||
}
|
||||
|
||||
stack->data[stack->count++] = value;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int prefix_code_stack_pop(PrefixCodeStack *stack)
|
||||
{
|
||||
if(stack->count == 0) return -1;
|
||||
return stack->data[--stack->count];
|
||||
}
|
||||
|
||||
static void prefix_code_stack_clear(PrefixCodeStack *stack) { stack->count = 0; }
|
||||
|
||||
// Bit reversal functions
|
||||
static uint32_t reverse_32(uint32_t val)
|
||||
{
|
||||
val = ((val >> 1) & 0x55555555) | ((val & 0x55555555) << 1);
|
||||
val = ((val >> 2) & 0x33333333) | ((val & 0x33333333) << 2);
|
||||
val = ((val >> 4) & 0x0F0F0F0F) | ((val & 0x0F0F0F0F) << 4);
|
||||
val = ((val >> 8) & 0x00FF00FF) | ((val & 0x00FF00FF) << 8);
|
||||
return (val >> 16) | (val << 16);
|
||||
}
|
||||
|
||||
static uint32_t reverse_n(uint32_t val, int length) { return reverse_32(val) >> (32 - length); }
|
||||
|
||||
// Table construction functions
|
||||
#define TABLE_MAX_SIZE 10
|
||||
|
||||
static void make_table(PrefixCode *code, int node, CodeTableEntry *table, int depth, int maxdepth)
|
||||
{
|
||||
int currtablesize = 1 << (maxdepth - depth);
|
||||
|
||||
if(is_invalid_node(code, node))
|
||||
{
|
||||
for(int i = 0; i < currtablesize; i++) table[i].length = -1;
|
||||
}
|
||||
else if(is_leaf_node(code, node))
|
||||
{
|
||||
for(int i = 0; i < currtablesize; i++)
|
||||
{
|
||||
table[i].length = depth;
|
||||
table[i].value = leaf_value(code, node);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if(depth == maxdepth)
|
||||
{
|
||||
table[0].length = maxdepth + 1;
|
||||
table[0].value = node;
|
||||
}
|
||||
else
|
||||
{
|
||||
make_table(code, left_branch(code, node), table, depth + 1, maxdepth);
|
||||
make_table(code, right_branch(code, node), table + currtablesize / 2, depth + 1, maxdepth);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void make_table_le(PrefixCode *code, int node, CodeTableEntry *table, int depth, int maxdepth)
|
||||
{
|
||||
int currtablesize = 1 << (maxdepth - depth);
|
||||
int currstride = 1 << depth;
|
||||
|
||||
if(is_invalid_node(code, node))
|
||||
{
|
||||
for(int i = 0; i < currtablesize; i++) table[i * currstride].length = -1;
|
||||
}
|
||||
else if(is_leaf_node(code, node))
|
||||
{
|
||||
for(int i = 0; i < currtablesize; i++)
|
||||
{
|
||||
table[i * currstride].length = depth;
|
||||
table[i * currstride].value = leaf_value(code, node);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if(depth == maxdepth)
|
||||
{
|
||||
table[0].length = maxdepth + 1;
|
||||
table[0].value = node;
|
||||
}
|
||||
else
|
||||
{
|
||||
make_table_le(code, left_branch(code, node), table, depth + 1, maxdepth);
|
||||
make_table_le(code, right_branch(code, node), table + currstride, depth + 1, maxdepth);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int prefix_code_make_table(PrefixCode *self)
|
||||
{
|
||||
if(self->table1) return PREFIX_CODE_OK;
|
||||
|
||||
if(self->maxlength < self->minlength)
|
||||
self->tablesize = TABLE_MAX_SIZE; // no code lengths recorded
|
||||
else if(self->maxlength >= TABLE_MAX_SIZE)
|
||||
self->tablesize = TABLE_MAX_SIZE;
|
||||
else
|
||||
self->tablesize = self->maxlength;
|
||||
|
||||
self->table1 = malloc(sizeof(CodeTableEntry) * (1 << self->tablesize));
|
||||
if(!self->table1) return PREFIX_CODE_INVALID;
|
||||
|
||||
make_table(self, 0, self->table1, 0, self->tablesize);
|
||||
return PREFIX_CODE_OK;
|
||||
}
|
||||
|
||||
static int prefix_code_make_table_le(PrefixCode *self)
|
||||
{
|
||||
if(self->table2) return PREFIX_CODE_OK;
|
||||
|
||||
if(self->maxlength < self->minlength)
|
||||
self->tablesize = TABLE_MAX_SIZE; // no code lengths recorded
|
||||
else if(self->maxlength >= TABLE_MAX_SIZE)
|
||||
self->tablesize = TABLE_MAX_SIZE;
|
||||
else
|
||||
self->tablesize = self->maxlength;
|
||||
|
||||
self->table2 = malloc(sizeof(CodeTableEntry) * (1 << self->tablesize));
|
||||
if(!self->table2) return PREFIX_CODE_INVALID;
|
||||
|
||||
make_table_le(self, 0, self->table2, 0, self->tablesize);
|
||||
return PREFIX_CODE_OK;
|
||||
}
|
||||
|
||||
// Public functions
|
||||
|
||||
PrefixCode *prefix_code_alloc(void)
|
||||
{
|
||||
PrefixCode *self = malloc(sizeof(PrefixCode));
|
||||
if(!self) return NULL;
|
||||
|
||||
self->tree = malloc(sizeof(CodeTreeNode));
|
||||
if(!self->tree)
|
||||
{
|
||||
free(self);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
set_empty_node(self, 0);
|
||||
self->numentries = 1;
|
||||
self->minlength = INT_MAX;
|
||||
self->maxlength = INT_MIN;
|
||||
self->isstatic = false;
|
||||
|
||||
self->stack = NULL;
|
||||
self->table1 = self->table2 = NULL;
|
||||
self->tablesize = 0;
|
||||
self->currnode = 0;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
PrefixCode *prefix_code_alloc_with_static_table(int (*statictable)[2])
|
||||
{
|
||||
PrefixCode *self = malloc(sizeof(PrefixCode));
|
||||
if(!self) return NULL;
|
||||
|
||||
self->tree = (CodeTreeNode *)statictable; // TODO: fix the ugly cast
|
||||
self->isstatic = true;
|
||||
|
||||
self->stack = NULL;
|
||||
self->table1 = self->table2 = NULL;
|
||||
self->tablesize = 0;
|
||||
self->currnode = 0;
|
||||
self->numentries = 0;
|
||||
self->minlength = INT_MAX;
|
||||
self->maxlength = INT_MIN;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
PrefixCode *prefix_code_alloc_with_lengths(const int *lengths, int numsymbols, int maxcodelength, bool zeros)
|
||||
{
|
||||
PrefixCode *self = prefix_code_alloc();
|
||||
if(!self) return NULL;
|
||||
|
||||
int code = 0, symbolsleft = numsymbols;
|
||||
|
||||
for(int length = 1; length <= maxcodelength; length++)
|
||||
{
|
||||
for(int i = 0; i < numsymbols; i++)
|
||||
{
|
||||
if(lengths[i] != length) continue;
|
||||
// Instead of reversing to get a low-bit-first code, we shift and use high-bit-first.
|
||||
int result;
|
||||
if(zeros) { result = prefix_code_add_value_high_bit_first(self, i, code, length); }
|
||||
else { result = prefix_code_add_value_high_bit_first(self, i, ~code, length); }
|
||||
if(result != PREFIX_CODE_OK)
|
||||
{
|
||||
prefix_code_free(self);
|
||||
return NULL;
|
||||
}
|
||||
code++;
|
||||
if(--symbolsleft == 0) return self; // early exit if all codes have been handled
|
||||
}
|
||||
code <<= 1;
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
void prefix_code_free(PrefixCode *self)
|
||||
{
|
||||
if(!self) return;
|
||||
|
||||
if(!self->isstatic) free(self->tree);
|
||||
free(self->table1);
|
||||
free(self->table2);
|
||||
if(self->stack) prefix_code_stack_free(self->stack);
|
||||
free(self);
|
||||
}
|
||||
|
||||
int prefix_code_add_value_high_bit_first(PrefixCode *self, int value, uint32_t code, int length)
|
||||
{
|
||||
return prefix_code_add_value_high_bit_first_repeat(self, value, code, length, length);
|
||||
}
|
||||
|
||||
int prefix_code_add_value_high_bit_first_repeat(PrefixCode *self, int value, uint32_t code, int length, int repeatpos)
|
||||
{
|
||||
if(!self || self->isstatic) return PREFIX_CODE_INVALID;
|
||||
|
||||
free(self->table1);
|
||||
free(self->table2);
|
||||
self->table1 = self->table2 = NULL;
|
||||
|
||||
if(length > self->maxlength) self->maxlength = length;
|
||||
if(length < self->minlength) self->minlength = length;
|
||||
|
||||
repeatpos = length - 1 - repeatpos;
|
||||
if(repeatpos == 0 ||
|
||||
(repeatpos >= 0 && (((code >> (repeatpos - 1)) & 3) == 0 || ((code >> (repeatpos - 1)) & 3) == 3)))
|
||||
{
|
||||
return PREFIX_CODE_INVALID;
|
||||
}
|
||||
|
||||
int lastnode = 0;
|
||||
for(int bitpos = length - 1; bitpos >= 0; bitpos--)
|
||||
{
|
||||
int bit = (code >> bitpos) & 1;
|
||||
|
||||
if(is_leaf_node(self, lastnode)) return PREFIX_CODE_INVALID;
|
||||
|
||||
if(bitpos == repeatpos)
|
||||
{
|
||||
if(!is_open_branch(self, lastnode, bit)) return PREFIX_CODE_INVALID;
|
||||
|
||||
int repeatnode = new_node(self);
|
||||
int nextnode = new_node(self);
|
||||
if(repeatnode < 0 || nextnode < 0) return PREFIX_CODE_INVALID;
|
||||
|
||||
set_branch(self, lastnode, bit, repeatnode);
|
||||
set_branch(self, repeatnode, bit, repeatnode);
|
||||
set_branch(self, repeatnode, bit ^ 1, nextnode);
|
||||
lastnode = nextnode;
|
||||
|
||||
bitpos++; // terminating bit already handled, skip it
|
||||
}
|
||||
else
|
||||
{
|
||||
if(is_open_branch(self, lastnode, bit))
|
||||
{
|
||||
int newnode = new_node(self);
|
||||
if(newnode < 0) return PREFIX_CODE_INVALID;
|
||||
set_branch(self, lastnode, bit, newnode);
|
||||
}
|
||||
lastnode = branch(self, lastnode, bit);
|
||||
}
|
||||
}
|
||||
|
||||
if(!is_empty_node(self, lastnode)) return PREFIX_CODE_INVALID;
|
||||
set_leaf_value(self, lastnode, value);
|
||||
return PREFIX_CODE_OK;
|
||||
}
|
||||
|
||||
int prefix_code_add_value_low_bit_first(PrefixCode *self, int value, uint32_t code, int length)
|
||||
{
|
||||
return prefix_code_add_value_high_bit_first(self, value, reverse_n(code, length), length);
|
||||
}
|
||||
|
||||
int prefix_code_add_value_low_bit_first_repeat(PrefixCode *self, int value, uint32_t code, int length, int repeatpos)
|
||||
{
|
||||
return prefix_code_add_value_high_bit_first_repeat(self, value, reverse_n(code, length), length, repeatpos);
|
||||
}
|
||||
|
||||
void prefix_code_start_building_tree(PrefixCode *self)
|
||||
{
|
||||
if(!self) return;
|
||||
|
||||
self->currnode = 0;
|
||||
if(!self->stack) { self->stack = prefix_code_stack_alloc(); }
|
||||
else { prefix_code_stack_clear(self->stack); }
|
||||
}
|
||||
|
||||
void prefix_code_start_zero_branch(PrefixCode *self)
|
||||
{
|
||||
if(!self) return;
|
||||
|
||||
int new = new_node(self);
|
||||
if(new < 0) return;
|
||||
|
||||
set_branch(self, self->currnode, 0, new);
|
||||
prefix_code_stack_push(self->stack, self->currnode);
|
||||
self->currnode = new;
|
||||
}
|
||||
|
||||
void prefix_code_start_one_branch(PrefixCode *self)
|
||||
{
|
||||
if(!self) return;
|
||||
|
||||
int new = new_node(self);
|
||||
if(new < 0) return;
|
||||
|
||||
set_branch(self, self->currnode, 1, new);
|
||||
prefix_code_stack_push(self->stack, self->currnode);
|
||||
self->currnode = new;
|
||||
}
|
||||
|
||||
void prefix_code_finish_branches(PrefixCode *self)
|
||||
{
|
||||
if(!self || !self->stack) return;
|
||||
|
||||
int node = prefix_code_stack_pop(self->stack);
|
||||
if(node >= 0) self->currnode = node;
|
||||
}
|
||||
|
||||
void prefix_code_make_leaf_with_value(PrefixCode *self, int value)
|
||||
{
|
||||
if(!self) return;
|
||||
|
||||
set_leaf_value(self, self->currnode, value);
|
||||
prefix_code_finish_branches(self);
|
||||
}
|
||||
|
||||
// BitStream interface functions
|
||||
|
||||
int prefix_code_read_symbol(BitStream *bs, PrefixCode *code)
|
||||
{
|
||||
if(!code) return PREFIX_CODE_INVALID;
|
||||
if(!code->table1)
|
||||
{
|
||||
if(prefix_code_make_table(code) != PREFIX_CODE_OK) return PREFIX_CODE_INVALID;
|
||||
}
|
||||
|
||||
int bits = bitstream_peek_bits(bs, code->tablesize);
|
||||
|
||||
int length = code->table1[bits].length;
|
||||
int value = code->table1[bits].value;
|
||||
|
||||
if(length < 0) return PREFIX_CODE_INVALID;
|
||||
|
||||
if(length <= code->tablesize)
|
||||
{
|
||||
bitstream_skip_bits(bs, length);
|
||||
return value;
|
||||
}
|
||||
|
||||
bitstream_skip_bits(bs, code->tablesize);
|
||||
|
||||
int node = value;
|
||||
while(!is_leaf_node(code, node))
|
||||
{
|
||||
int bit = bitstream_read_bit(bs);
|
||||
if(is_open_branch(code, node, bit)) return PREFIX_CODE_INVALID;
|
||||
node = branch(code, node, bit);
|
||||
}
|
||||
return leaf_value(code, node);
|
||||
}
|
||||
|
||||
int prefix_code_read_symbol_le(BitStream *bs, PrefixCode *code)
|
||||
{
|
||||
if(!code) return PREFIX_CODE_INVALID;
|
||||
if(!code->table2)
|
||||
{
|
||||
if(prefix_code_make_table_le(code) != PREFIX_CODE_OK) return PREFIX_CODE_INVALID;
|
||||
}
|
||||
|
||||
int bits = bitstream_peek_bits_le(bs, code->tablesize);
|
||||
|
||||
int length = code->table2[bits].length;
|
||||
int value = code->table2[bits].value;
|
||||
|
||||
if(length < 0) return PREFIX_CODE_INVALID;
|
||||
|
||||
if(length <= code->tablesize)
|
||||
{
|
||||
bitstream_skip_bits_le(bs, length);
|
||||
return value;
|
||||
}
|
||||
|
||||
bitstream_skip_bits_le(bs, code->tablesize);
|
||||
|
||||
int node = value;
|
||||
while(!is_leaf_node(code, node))
|
||||
{
|
||||
int bit = bitstream_read_bit_le(bs);
|
||||
if(is_open_branch(code, node, bit)) return PREFIX_CODE_INVALID;
|
||||
node = branch(code, node, bit);
|
||||
}
|
||||
return leaf_value(code, node);
|
||||
}
|
||||
89
pak/prefixcode.h
Normal file
89
pak/prefixcode.h
Normal file
@@ -0,0 +1,89 @@
|
||||
/*
|
||||
* prefixcode.h - Prefix code tree implementation
|
||||
*
|
||||
* Copyright (c) 2017-present, MacPaw Inc. All rights reserved.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||
* MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#ifndef PREFIXCODE_H
|
||||
#define PREFIXCODE_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include "bitstream.h"
|
||||
|
||||
// Error codes
|
||||
#define PREFIX_CODE_OK 0
|
||||
#define PREFIX_CODE_INVALID -1
|
||||
|
||||
typedef struct CodeTreeNode
|
||||
{
|
||||
int branches[2];
|
||||
} CodeTreeNode;
|
||||
|
||||
typedef struct CodeTableEntry
|
||||
{
|
||||
uint32_t length;
|
||||
int32_t value;
|
||||
} CodeTableEntry;
|
||||
|
||||
// Simple stack implementation for tree building
|
||||
typedef struct PrefixCodeStack
|
||||
{
|
||||
int *data;
|
||||
int count;
|
||||
int capacity;
|
||||
} PrefixCodeStack;
|
||||
|
||||
typedef struct PrefixCode
|
||||
{
|
||||
CodeTreeNode *tree;
|
||||
int numentries;
|
||||
int minlength;
|
||||
int maxlength;
|
||||
bool isstatic;
|
||||
|
||||
int currnode;
|
||||
PrefixCodeStack *stack;
|
||||
|
||||
int tablesize;
|
||||
CodeTableEntry *table1;
|
||||
CodeTableEntry *table2;
|
||||
} PrefixCode;
|
||||
|
||||
// Function declarations
|
||||
PrefixCode *prefix_code_alloc(void);
|
||||
PrefixCode *prefix_code_alloc_with_lengths(const int *lengths, int numsymbols, int maxlength, bool shortestCodeIsZeros);
|
||||
PrefixCode *prefix_code_alloc_with_static_table(int (*statictable)[2]);
|
||||
void prefix_code_free(PrefixCode *self);
|
||||
|
||||
int prefix_code_add_value_high_bit_first(PrefixCode *self, int value, uint32_t code, int length);
|
||||
int prefix_code_add_value_high_bit_first_repeat(PrefixCode *self, int value, uint32_t code, int length, int repeatpos);
|
||||
int prefix_code_add_value_low_bit_first(PrefixCode *self, int value, uint32_t code, int length);
|
||||
int prefix_code_add_value_low_bit_first_repeat(PrefixCode *self, int value, uint32_t code, int length, int repeatpos);
|
||||
|
||||
void prefix_code_start_building_tree(PrefixCode *self);
|
||||
void prefix_code_start_zero_branch(PrefixCode *self);
|
||||
void prefix_code_start_one_branch(PrefixCode *self);
|
||||
void prefix_code_finish_branches(PrefixCode *self);
|
||||
void prefix_code_make_leaf_with_value(PrefixCode *self, int value);
|
||||
|
||||
// BitStream interface functions
|
||||
int prefix_code_read_symbol(BitStream *bs, PrefixCode *code);
|
||||
int prefix_code_read_symbol_le(BitStream *bs, PrefixCode *code);
|
||||
|
||||
#endif /* PREFIXCODE_H */
|
||||
@@ -60,9 +60,21 @@ file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/data/arccrunch_dynamic.bin
|
||||
file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/data/arcsquash.bin
|
||||
DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/data/)
|
||||
|
||||
file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/data/pak_crush.bin
|
||||
DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/data/)
|
||||
|
||||
file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/data/pak_distill.bin
|
||||
DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/data/)
|
||||
|
||||
file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/data/ha_asc.bin
|
||||
DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/data/)
|
||||
|
||||
file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/data/ha_hsc.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
|
||||
zoo/lzd.cpp arc/pack.cpp lh5.cpp arc/squeeze.cpp arc/crunch.cpp
|
||||
arc/squash.cpp)
|
||||
zoo/lzd.cpp arc/pack.cpp lh5.cpp arc/squeeze.cpp arc/crunch.cpp arc/squash.cpp pak/crush.cpp
|
||||
pak/distill.cpp ha.cpp)
|
||||
target_link_libraries(tests_run gtest gtest_main "Aaru.Compression.Native")
|
||||
|
||||
BIN
tests/data/amg.bin
Executable file
BIN
tests/data/amg.bin
Executable file
Binary file not shown.
BIN
tests/data/pak_crush.bin
Normal file
BIN
tests/data/pak_crush.bin
Normal file
Binary file not shown.
BIN
tests/data/pak_distill.bin
Normal file
BIN
tests/data/pak_distill.bin
Normal file
Binary file not shown.
133
tests/ha.cpp
Normal file
133
tests/ha.cpp
Normal file
@@ -0,0 +1,133 @@
|
||||
/*
|
||||
* This file is part of the Aaru Data Preservation Suite.
|
||||
* Copyright (c) 2019-2025 Natalia Portillo.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as
|
||||
* published by the Free Software Foundation; either version 2.1 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <climits>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
|
||||
#include "../library.h"
|
||||
#include "crc32.h"
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
#define EXPECTED_CRC32 0x66007dba
|
||||
|
||||
static const uint8_t *buffer;
|
||||
|
||||
class ha_ascFixture : public ::testing::Test
|
||||
{
|
||||
public:
|
||||
ha_ascFixture()
|
||||
{
|
||||
// initialization;
|
||||
// can also be done in SetUp()
|
||||
}
|
||||
|
||||
protected:
|
||||
void SetUp()
|
||||
{
|
||||
char path[PATH_MAX];
|
||||
char filename[PATH_MAX];
|
||||
|
||||
getcwd(path, PATH_MAX);
|
||||
snprintf(filename, PATH_MAX, "%s/data/ha_asc.bin", path);
|
||||
|
||||
FILE *file = fopen(filename, "rb");
|
||||
buffer = (const uint8_t *)malloc(53654);
|
||||
fread((void *)buffer, 1, 53654, file);
|
||||
fclose(file);
|
||||
}
|
||||
|
||||
void TearDown() { free((void *)buffer); }
|
||||
|
||||
~ha_ascFixture()
|
||||
{
|
||||
// resources cleanup, no exceptions allowed
|
||||
}
|
||||
|
||||
// shared user data
|
||||
};
|
||||
|
||||
TEST_F(ha_ascFixture, ha_asc)
|
||||
{
|
||||
size_t destLen = 152089;
|
||||
size_t srcLen = 53654;
|
||||
auto * outBuf = (uint8_t *)malloc(53654);
|
||||
|
||||
auto err = ha_asc_decompress(buffer, srcLen, outBuf, &destLen);
|
||||
|
||||
EXPECT_EQ(err, 0);
|
||||
EXPECT_EQ(destLen, 152089);
|
||||
|
||||
auto crc = crc32_data(outBuf, 152089);
|
||||
|
||||
free(outBuf);
|
||||
|
||||
EXPECT_EQ(crc, EXPECTED_CRC32);
|
||||
}
|
||||
|
||||
class ha_hscFixture : public ::testing::Test
|
||||
{
|
||||
public:
|
||||
ha_hscFixture()
|
||||
{
|
||||
// initialization;
|
||||
// can also be done in SetUp()
|
||||
}
|
||||
|
||||
protected:
|
||||
void SetUp()
|
||||
{
|
||||
char path[PATH_MAX];
|
||||
char filename[PATH_MAX];
|
||||
|
||||
getcwd(path, PATH_MAX);
|
||||
snprintf(filename, PATH_MAX, "%s/data/ha_hsc.bin", path);
|
||||
|
||||
FILE *file = fopen(filename, "rb");
|
||||
buffer = (const uint8_t *)malloc(41839);
|
||||
fread((void *)buffer, 1, 41839, file);
|
||||
fclose(file);
|
||||
}
|
||||
|
||||
void TearDown() { free((void *)buffer); }
|
||||
|
||||
~ha_hscFixture()
|
||||
{
|
||||
// resources cleanup, no exceptions allowed
|
||||
}
|
||||
|
||||
// shared user data
|
||||
};
|
||||
|
||||
TEST_F(ha_hscFixture, ha_hsc)
|
||||
{
|
||||
size_t destLen = 152089;
|
||||
size_t srcLen = 41839;
|
||||
auto * outBuf = (uint8_t *)malloc(152089);
|
||||
|
||||
auto err = ha_hsc_decompress(buffer, srcLen, outBuf, &destLen);
|
||||
|
||||
EXPECT_EQ(err, 0);
|
||||
EXPECT_EQ(destLen, 152089);
|
||||
|
||||
auto crc = crc32_data(outBuf, 152089);
|
||||
|
||||
free(outBuf);
|
||||
|
||||
EXPECT_EQ(crc, EXPECTED_CRC32);
|
||||
}
|
||||
Submodule tests/lib updated: 16f637fbf4...614f05d1c0
81
tests/pak/crush.cpp
Normal file
81
tests/pak/crush.cpp
Normal file
@@ -0,0 +1,81 @@
|
||||
/*
|
||||
* This file is part of the Aaru Data Preservation Suite.
|
||||
* Copyright (c) 2019-2025 Natalia Portillo.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as
|
||||
* published by the Free Software Foundation; either version 2.1 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <climits>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
|
||||
#include "../../library.h"
|
||||
#include "../crc32.h"
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
#define EXPECTED_CRC32 0x66007dba
|
||||
|
||||
static const uint8_t *buffer;
|
||||
|
||||
class crushFixture : public ::testing::Test
|
||||
{
|
||||
public:
|
||||
crushFixture()
|
||||
{
|
||||
// initialization;
|
||||
// can also be done in SetUp()
|
||||
}
|
||||
|
||||
protected:
|
||||
void SetUp()
|
||||
{
|
||||
char path[PATH_MAX];
|
||||
char filename[PATH_MAX];
|
||||
|
||||
getcwd(path, PATH_MAX);
|
||||
snprintf(filename, PATH_MAX, "%s/data/pak_crush.bin", path);
|
||||
|
||||
FILE *file = fopen(filename, "rb");
|
||||
buffer = (const uint8_t *)malloc(63282);
|
||||
fread((void *)buffer, 1, 63282, file);
|
||||
fclose(file);
|
||||
}
|
||||
|
||||
void TearDown() { free((void *)buffer); }
|
||||
|
||||
~crushFixture()
|
||||
{
|
||||
// resources cleanup, no exceptions allowed
|
||||
}
|
||||
|
||||
// shared user data
|
||||
};
|
||||
|
||||
TEST_F(crushFixture, crush)
|
||||
{
|
||||
size_t destLen = 152089;
|
||||
size_t srcLen = 63282;
|
||||
auto *outBuf = (uint8_t *)malloc(152089);
|
||||
|
||||
auto err = pak_decompress_crush(buffer, srcLen, outBuf, &destLen);
|
||||
|
||||
EXPECT_EQ(err, 0);
|
||||
EXPECT_EQ(destLen, 152089);
|
||||
|
||||
auto crc = crc32_data(outBuf, 152089);
|
||||
|
||||
free(outBuf);
|
||||
|
||||
EXPECT_EQ(crc, EXPECTED_CRC32);
|
||||
}
|
||||
81
tests/pak/distill.cpp
Normal file
81
tests/pak/distill.cpp
Normal file
@@ -0,0 +1,81 @@
|
||||
/*
|
||||
* This file is part of the Aaru Data Preservation Suite.
|
||||
* Copyright (c) 2019-2025 Natalia Portillo.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as
|
||||
* published by the Free Software Foundation; either version 2.1 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <climits>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
|
||||
#include "../../library.h"
|
||||
#include "../crc32.h"
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
#define EXPECTED_CRC32 0x66007dba
|
||||
|
||||
static const uint8_t *buffer;
|
||||
|
||||
class distillFixture : public ::testing::Test
|
||||
{
|
||||
public:
|
||||
distillFixture()
|
||||
{
|
||||
// initialization;
|
||||
// can also be done in SetUp()
|
||||
}
|
||||
|
||||
protected:
|
||||
void SetUp()
|
||||
{
|
||||
char path[PATH_MAX];
|
||||
char filename[PATH_MAX];
|
||||
|
||||
getcwd(path, PATH_MAX);
|
||||
snprintf(filename, PATH_MAX, "%s/data/pak_distill.bin", path);
|
||||
|
||||
FILE *file = fopen(filename, "rb");
|
||||
buffer = (const uint8_t *)malloc(60540);
|
||||
fread((void *)buffer, 1, 60540, file);
|
||||
fclose(file);
|
||||
}
|
||||
|
||||
void TearDown() { free((void *)buffer); }
|
||||
|
||||
~distillFixture()
|
||||
{
|
||||
// resources cleanup, no exceptions allowed
|
||||
}
|
||||
|
||||
// shared user data
|
||||
};
|
||||
|
||||
TEST_F(distillFixture, distill)
|
||||
{
|
||||
size_t destLen = 152089;
|
||||
size_t srcLen = 60540;
|
||||
auto *outBuf = (uint8_t *)malloc(152089);
|
||||
|
||||
auto err = pak_decompress_distill(buffer, srcLen, outBuf, &destLen);
|
||||
|
||||
EXPECT_EQ(err, 0);
|
||||
EXPECT_EQ(destLen, 152089);
|
||||
|
||||
auto crc = crc32_data(outBuf, 152089);
|
||||
|
||||
free(outBuf);
|
||||
|
||||
EXPECT_EQ(crc, EXPECTED_CRC32);
|
||||
}
|
||||
Reference in New Issue
Block a user