Implement HA's ASC and HSC algorithms.

This commit is contained in:
2025-09-06 18:59:18 +01:00
parent 4df2934506
commit ce378021ba
15 changed files with 1629 additions and 6 deletions

View File

@@ -132,7 +132,17 @@ add_library("Aaru.Compression.Native" SHARED library.c apple_rle.c apple_rle.h a
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)
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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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 */

View File

@@ -150,4 +150,16 @@ AARU_EXPORT int AARU_CALL pak_decompress_crush(const unsigned char *in_buf, size
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

View File

@@ -66,11 +66,15 @@ file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/data/pak_crush.bin
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
pak/crush.cpp
pak/distill.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")

133
tests/ha.cpp Normal file
View 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);
}