XADMaster/libxad/clients/Zoom.c

1496 lines
41 KiB
C
Executable file

#ifndef XADMASTER_ZOOM_C
#define XADMASTER_ZOOM_C
/* $Id: Zoom.c,v 1.13 2005/06/23 14:54:41 stoecker Exp $
Zoom disk archiver client
XAD library system for archive handling
Copyright (C) 1998 and later by Dirk Stöcker <soft@dstoecker.de>
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 "../unix/xadClient.h"
#include "xadCRC_1021.c"
#ifndef XADMASTERVERSION
#define XADMASTERVERSION 13
#endif
XADCLIENTVERSTR("Zoom 1.11 (21.2.2004)")
#define ZOOM_VERSION 1
#define ZOOM_REVISION 11
#define ZOOM5_VERSION ZOOM_VERSION
#define ZOOM5_REVISION ZOOM_REVISION
#define LHPAK_VERSION ZOOM_VERSION
#define LHPAK_REVISION ZOOM_REVISION
#define PCOMP_VERSION ZOOM_VERSION
#define PCOMP_REVISION ZOOM_REVISION
#define SOMNI_VERSION ZOOM_VERSION
#define SOMNI_REVISION ZOOM_REVISION
#define LHSFX_VERSION ZOOM_VERSION
#define LHSFX_REVISION ZOOM_REVISION
#define BOXSIZE 5
#define TRACKSIZE (512*11*2)
struct TrackHeader
{
xadINT8 Tracks[BOXSIZE];
xadUINT32 Bits[BOXSIZE];
xadUINT16 Length;
xadUINT16 SqueezedLength;
xadUINT16 FatLength;
xadUINT16 Packed;
xadUINT32 CRC;
xadUINT16 Bytes;
};
struct OldTrackHeader
{
xadINT8 Tracks[BOXSIZE];
xadUINT32 Bits[BOXSIZE];
xadUINT16 Length; /* entry length */
xadUINT8 Bytes[2];
xadUINT16 SqueezedLength; /* length after RLE */
xadUINT16 FatLength; /* Original size */
xadUINT16 Packed; /* LH packed ? */
xadUINT16 CRC;
};
struct ZoomNote
{
xadUINT8 DateStamp[12];
xadUINT8 Note[80];
};
struct ZoomHeader
{
xadUINT32 HeaderID; /* Should be `ZOM5' */
xadINT8 From;
xadINT8 To; /* First and last track. */
xadINT8 Version; /* Which Zoom version created this file. */
xadINT8 OnlyTracks; /* TRUE if file only contains whole tracks. */
xadUINT32 Flags; /* Miscellaneous flag bits. */
/* FIXME */
xadUINT8 Date[12]; /* File creation date. */
xadUINT32 TextLength; /* Size of text attached to this file. */
xadUINT32 PackedLength; /* Compressed size of text attached to this file. */
xadINT32 NoteOffset; /* Notes attached to this file. */
xadINT8 Encrypted; /* TRUE if file is encrypted with password. */
xadINT8 Format;
xadINT16 Reserved1; /* Reserved data. */
xadUINT32 root; /* Encryption key. */
xadUINT32 Reserved2[7]; /* Reserved data. */
};
/* ZoomHeader->Flags */
#define USEHEADER 1
/***************** LZSS Parameters ***************/
#define SeqMax 60 /* MaxSize of sequence (look-ahead depth) */
/************* Huffman coding parameters *********/
#define AnzChar (256+SeqMax+1) /* character code (= 0..AnzChar-1) */
#define SizeTable (AnzChar*2-1) /* Size of table */
#define Root (SizeTable-1) /* root position */
#define frequenzlimit 0x8000 /* stop updating if cumulative frequency reaches to this value */
/* ------------- DECODE - TABELLE (code,len) ----------------- */
static const xadUINT16 d_code_len[256] = {
0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
0x0101,0x0101,0x0101,0x0101,0x0101,0x0101,0x0101,0x0101,
0x0101,0x0101,0x0101,0x0101,0x0101,0x0101,0x0101,0x0101,
0x0201,0x0201,0x0201,0x0201,0x0201,0x0201,0x0201,0x0201,
0x0201,0x0201,0x0201,0x0201,0x0201,0x0201,0x0201,0x0201,
0x0301,0x0301,0x0301,0x0301,0x0301,0x0301,0x0301,0x0301,
0x0301,0x0301,0x0301,0x0301,0x0301,0x0301,0x0301,0x0301,
0x0402,0x0402,0x0402,0x0402,0x0402,0x0402,0x0402,0x0402,
0x0502,0x0502,0x0502,0x0502,0x0502,0x0502,0x0502,0x0502,
0x0602,0x0602,0x0602,0x0602,0x0602,0x0602,0x0602,0x0602,
0x0702,0x0702,0x0702,0x0702,0x0702,0x0702,0x0702,0x0702,
0x0802,0x0802,0x0802,0x0802,0x0802,0x0802,0x0802,0x0802,
0x0902,0x0902,0x0902,0x0902,0x0902,0x0902,0x0902,0x0902,
0x0A02,0x0A02,0x0A02,0x0A02,0x0A02,0x0A02,0x0A02,0x0A02,
0x0B02,0x0B02,0x0B02,0x0B02,0x0B02,0x0B02,0x0B02,0x0B02,
0x0C03,0x0C03,0x0C03,0x0C03,0x0D03,0x0D03,0x0D03,0x0D03,
0x0E03,0x0E03,0x0E03,0x0E03,0x0F03,0x0F03,0x0F03,0x0F03,
0x1003,0x1003,0x1003,0x1003,0x1103,0x1103,0x1103,0x1103,
0x1203,0x1203,0x1203,0x1203,0x1303,0x1303,0x1303,0x1303,
0x1403,0x1403,0x1403,0x1403,0x1503,0x1503,0x1503,0x1503,
0x1603,0x1603,0x1603,0x1603,0x1703,0x1703,0x1703,0x1703,
0x1804,0x1804,0x1904,0x1904,0x1A04,0x1A04,0x1B04,0x1B04,
0x1C04,0x1C04,0x1D04,0x1D04,0x1E04,0x1E04,0x1F04,0x1F04,
0x2004,0x2004,0x2104,0x2104,0x2204,0x2204,0x2304,0x2304,
0x2404,0x2404,0x2504,0x2504,0x2604,0x2604,0x2704,0x2704,
0x2804,0x2804,0x2904,0x2904,0x2A04,0x2A04,0x2B04,0x2B04,
0x2C04,0x2C04,0x2D04,0x2D04,0x2E04,0x2E04,0x2F04,0x2F04,
0x3005,0x3105,0x3205,0x3305,0x3405,0x3505,0x3605,0x3705,
0x3805,0x3905,0x3A05,0x3B05,0x3C05,0x3D05,0x3E05,0x3F05,
};
struct LhData {
xadUINT16 freq[SizeTable+1];
xadINT16 par_back[AnzChar];
xadINT16 parent[SizeTable];
xadINT16 son[SizeTable];
};
static void Update(struct LhData *dat, xadINT16 pos)
{
xadINT16 i, j, k, l;
xadUINT16 *freq;
i = dat->parent[pos];
do
{
freq = &dat->freq[i]; /* Mutterknotenhäufigkeit */
j = *freq;
*(freq++) = ++j; /* Mutterknotenhäfigkeit erhöhen */
if(*(freq++) < j) /* hat der Eintrag die Continuität beeinträchtigt ? */
{
/* dann müssen Knoten vertauscht werden, d.h. die Knoten werden umgehängt */
/* i == Swap Knoten 1 */
while(*freq < j)
++freq; /* swap Knoten 2 suchen */
--freq; /* freq == Swapknoten 2 */
k = freq - dat->freq; /* k == (offset zum Swap 2) */
dat->freq[i] = *freq; /* freq(knot1)=freq(knot2) */
*freq = j; /* freq(knot2)=freq(knot1) */
if((j = dat->son[i]) >= 0) /* wenn der l-sohn vom old knot ein code ist */
dat->parent[j+1] = k; /* dann neuer r-Vater=new knot */
dat->parent[j] = k; /* jf. neuer l-vater=new knot */
if((l = dat->son[k]) >= 0) /* wenn der l-sohn vom new knot ein code ist */
dat->parent[l+1] = i; /* dann neuer r-Vater=old knot */
dat->son[k] = j; /* l-sohn(new knot)=l-sohn(old k) */
dat->parent[l] = i; /* l-vater von sohn vom new knot = old knot */
dat->son[i] = l; /* l-sohn vom old knot= l-sohn vom new knot */
i = dat->parent[k]; /* vater holen */
}
else
i = dat->parent[i]; /* vater holen */
} while(i); /* bis Root erreicht ist. */
}
static void InitFreqTree(struct LhData *dat)
{
/* Ersteinmal müssen die Codes '-parent' in Verbindung mit 'son' gesetzt werden.
Außerdem wird die globale Häufigkeit 'freq' auf eins initialisiert. */
xadINT16 i, j, k;
k = 0; /* Zeigerwert von parent auf son */
j = -1; /* Zeigerwert von son auf parent */
for(i = 0; i < AnzChar; ++i)
{
dat->freq[i] = 1; /* Häufigkeit = 1 */
dat->son[i] = j--; /* leafs = SizeTable. SizeTable.+1.. */
dat->parent[-i-1] = k++; /* parentpointer initialisieren (T*2) */
}
k = 0;
j = AnzChar;
for(i = 0; i < Root-AnzChar+1; ++i)
{
dat->freq[j] = dat->freq[k] + dat->freq[k+1];
dat->son[j] = k; /* SONeinträge (ab AnzChar.) */
dat->parent[k] = dat->parent[k+1] = j++; /* Parenteinträge (ab 0.) */
k += 2;
}
dat->freq[SizeTable] = 0xFFFF;
dat->parent[Root] = 0;
}
static xadINT32 LhDecode(xadUINT16 *src, xadUINT32 srcsize, xadSTRPTR dest, xadUINT32 destsize, struct xadMasterBase *xadMasterBase)
{
xadINT16 bitindex, i, j, k, l;
xadINT32 err = 0;
xadUINT32 bitbuf;
xadUINT8 *str;
struct LhData *dat;
xadUINT16 *srcend;
srcend = (xadUINT16 *) (((xadSTRPTR) src) + srcsize);
if(!(dat = (struct LhData *) xadAllocVec(XADM sizeof(struct LhData), XADMEMF_ANY)))
return XADERR_NOMEMORY;
InitFreqTree(dat);
bitindex = 15; /* getlen = bitindex */
bitbuf = *(src++); /* prestore getbuf */
while(!err)
{
/**** search FreqTree from root to leaves ****/
/**** choose node son() IF bit == 0 ****/
/**** else choose son()+1 IF bit == 1 ****/
l = dat->son[Root]; /* son(R) erster Ast */
do
{
l = dat->son[l + ((bitbuf&0x8000) ? 1 : 0)];
bitbuf <<= 1;
if(!(bitindex--)) /* bitcounter runterzählen */
{
bitindex = 15;
bitbuf = *(src++);
}
} while(l >= 0);
if(dat->freq[Root] != frequenzlimit)
Update(dat, l);
if(l >= -256)
{
if(!(destsize--))
err = XADERR_DECRUNCH;
else
*(dest++) = -l - 1;
}
else if(l <= -(255+SeqMax+1)-1)
break; /* this is the leave point */
else /***** DecodePosition *****/
{
bitindex -= 7;
if(bitindex < 0)
{
bitbuf = (bitbuf << 8) | (*(src++) << -bitindex);
bitindex += 15; /* correct bitindex */
i = (xadUINT8) (bitbuf >> 16); /* get byte */
}
else
{
i = (xadUINT8) (bitbuf>>8); /* get upper byte */
bitbuf <<= 8; /* shift byte out */
if(!(bitindex--))
{
bitbuf = *(src++);
bitindex = 15;
}
}
k = d_code_len[i];
j = (xadUINT8) k; /* j: Anzahl der kommenden Bits */
k = (k&0xFF00) >> 2; /* k = (d_code(i) << 6) */
do
{
i <<= 1;
if(bitbuf & 0x8000) /* bit einfuegen */
++i;
bitbuf <<= 1; /* und bit rausschieben */
if(!(bitindex--))
{
bitbuf = *(src++);
bitindex = 15;
}
} while(j--);
str = (xadUINT8 *) dest - ((i&0x3F)|k); /* source-position */
l = -(l+256);
if(l > destsize)
err = XADERR_DECRUNCH;
else
{
destsize -= l;
while(l--)
*(dest++) = *(str++);
}
}
}
if(src != srcend || destsize)
err = XADERR_DECRUNCH;
xadFreeObjectA(XADM dat, 0);
return err;
}
static xadUINT32 NewKey(xadSTRPTR Key)
{
xadUINT32 root, i = 1;
root = *Key; /* use first entry as init value */
while(Key[i])
root *= Key[i++];
return root;
}
static void CryptKeyBlock(xadSTRPTR Block, xadINT32 Length, xadSTRPTR Key)
{
xadUINT32 root, i = 0;
root = NewKey(Key);
while(Length--)
{
root = (((root * 4253261) & 0xFFFFFF) + 372837) & 0xFFFFFF;
*Block++ ^= (root % 256) ^ (~Key[i++]);
if(!Key[i])
i = 0;
}
}
/* oldmode == 100, 101 means direct CRC check, 200 no check */
static xadINT32 CheckCRC(xadPTR buf, xadUINT32 size, xadUINT32 oldmode, xadUINT32 CRC,
struct xadArchiveInfo *ai, struct xadMasterBase *xadMasterBase)
{
xadINT32 err = 0;
if(oldmode == 200)
err = 0;
else if(oldmode == 100)
{
if(xadCalcCRC32(XADM XADCRC32_ID1, 0, size, buf) != CRC)
err = XADERR_CHECKSUM;
}
else if(oldmode == 101)
{
if(xadCRC_1021_2(buf, size) != CRC)
err = XADERR_CHECKSUM;
}
else if(oldmode)
{
xadUINT16 a;
if(!(err = xadHookAccess(XADM XADAC_READ, 2, &a, ai)))
{
if(xadCRC_1021_2(buf, size) != a)
err = XADERR_CHECKSUM;
}
}
else
{
xadUINT32 a;
if(!(err = xadHookAccess(XADM XADAC_READ, 4, &a, ai)))
{
if(xadCalcCRC32(XADM XADCRC32_ID1, 0, size, buf) != a)
err = XADERR_CHECKSUM;
}
}
return err;
}
/* reads lh data from current input stream and stores a pointer to
decrunched file in *str */
static xadINT32 DoDecrunch(xadSTRPTR *str, xadUINT32 insize, xadUINT32 ressize, xadUINT32 oldmode,
struct xadArchiveInfo *ai, struct xadMasterBase *xadMasterBase, xadUINT32 CRC)
{
xadUINT16 *mem;
xadINT32 err = XADERR_NOMEMORY;
*str = 0;
if((mem = (xadUINT16 *) xadAllocVec(XADM insize, XADMEMF_PUBLIC)))
{
if(!(err = xadHookAccess(XADM XADAC_READ, insize, mem, ai)))
{
xadSTRPTR mem2;
if(!(err = CheckCRC(mem, insize, oldmode, CRC, ai, xadMasterBase)))
{
if((mem2 = (xadSTRPTR) xadAllocVec(XADM ressize+1, XADMEMF_PUBLIC|XADMEMF_CLEAR)))
{
if((err = LhDecode(mem, insize, mem2, ressize, xadMasterBase)))
xadFreeObjectA(XADM mem2, 0);
else
*str = mem2;
}
else
err = XADERR_NOMEMORY;
} /* xadAllocVec */
} /* Hook Read */
xadFreeObjectA(XADM mem, 0);
} /* xadAllocVec */
return err;
}
static xadINT32 RunDecode(xadSTRPTR src, xadSTRPTR dest, xadUINT32 size)
{
xadSTRPTR end;
xadINT16 b, c, p;
end = dest + (*(src++) << 16); /* Get Size */
end += *(src++) << 8;
end += *(src++);
p = *(src++); /* get packbyte */
if(end-dest != size)
return XADERR_DECRUNCH;
while(dest < end) /* reached end? */
{
b = *(src++); /* get next byte */
if(b == p) /* is packbyte ? */
{
if((b = *(src++)))
{
c = *(src++); /* get byte of sequenz */
if(++b > end-dest) /* prevent overwriting */
b = end-dest;
while(b--) /* write it */
*(dest++) = c;
}
else
*(dest++) = p; /* write packbyte one time */
}
else
*(dest++) = b; /* write byte */
}
return 0;
}
static xadINT32 RunDecodeOld(xadSTRPTR src, xadSTRPTR dest, xadUINT32 size, xadUINT32 srcsize, xadUINT16 Bytes)
{
xadSTRPTR s, d;
xadUINT32 cnt;
xadUINT8 b1, b2, b;
b1 = Bytes>>8;
b2 = Bytes;
s = src + srcsize;
d = dest + size;
while(d > dest) /* reached end? */
{
b = *(--s);
if(b == b1)
{
if(!(cnt = *(--s)))
cnt = 0x100;
b = *(--s);
if(cnt > d-dest)
return XADERR_DECRUNCH;
while(cnt--)
*(--d) = b;
}
else if(b == b2)
{
if(!(cnt = *(--s)))
cnt = 0x100;
while(cnt--)
*(--d) = 0;
}
else
*(--d) = b; /* write byte */
}
if(s < src)
return XADERR_DECRUNCH;
return 0;
}
static xadINT32 DoDecrunchZoom(xadSTRPTR *str, struct TrackHeader *th, xadUINT32 oldmode, xadSTRPTR pwd,
struct xadArchiveInfo *ai, struct xadMasterBase *xadMasterBase)
{
xadPTR mem;
xadINT32 err = XADERR_NOMEMORY;
*str = 0;
if((mem = xadAllocVec(XADM th->Length, XADMEMF_PUBLIC)))
{
if(!(err = xadHookAccess(XADM XADAC_READ, th->Length, mem, ai)))
{
if(!(err = CheckCRC(mem, th->Length, oldmode ? 101 : 100, th->CRC, ai, xadMasterBase)))
{
if(pwd)
CryptKeyBlock(mem, th->Length, pwd);
if(th->Packed)
{
xadSTRPTR mem2;
xadUINT32 size;
if(!(size = th->SqueezedLength))
size = th->FatLength;
if((mem2 = (xadSTRPTR) xadAllocVec(XADM size, XADMEMF_PUBLIC|XADMEMF_CLEAR)))
{
err = LhDecode(mem, th->Length, mem2, size, xadMasterBase);
xadFreeObjectA(XADM mem, 0);
mem = mem2;
}
else
err = XADERR_NOMEMORY;
}
if(!err && th->SqueezedLength)
{
xadPTR mem2;
if((mem2 = xadAllocVec(XADM th->FatLength, XADMEMF_PUBLIC|XADMEMF_CLEAR)))
{
if(oldmode)
err = RunDecodeOld(mem, mem2, th->FatLength, th->SqueezedLength, th->Bytes);
else
err = RunDecode(mem, mem2, th->FatLength);
xadFreeObjectA(XADM mem, 0);
mem = mem2;
}
else
err = XADERR_NOMEMORY;
}
} /* CRC check */
} /* read data */
} /* allocate buffer */
if(mem)
{
if(err)
xadFreeObjectA(XADM mem, 0);
else
*str = mem;
}
return err;
}
static void ClearMemLong(xadUINT32 *a, xadUINT32 size)
{
while(size--)
*(a++) = 0;
}
static void UnPackTrack(struct TrackHeader *th, xadSTRPTR buf, xadSTRPTR tmp, xadUINT32 pos,
struct xadMasterBase *xadMasterBase)
{
xadINT32 i, j = 0, k, l;
for(i = 0; i < pos; ++i)
{
l = th->Bits[i];
for(k = 0; k < 22; ++k)
{
if(l & 1)
++j;
l >>= 1;
}
}
buf += j*512;
l = th->Bits[pos];
for(i = j = 0; i < 22 ; ++i)
{
if(l & (1<<i))
xadCopyMem(XADM buf + 512*(j++), tmp + (512*i), 512);
else
ClearMemLong((xadUINT32 *) (tmp+(512*i)), 512/4);
}
}
/**************************************************************************/
XADRECOGDATA(Zoom5)
{
if(data[0] == 'Z' && data[1] == 'O' && data[2] == 'M' && data[3] == '5'
&& xadCalcCRC32(XADM XADCRC32_ID1, 0, sizeof(struct ZoomHeader), data)
== (EndGetM32(data+72)))
return 1;
else
return 0;
}
XADRECOGDATA(Zoom)
{
if(data[0] == 'Z' && data[1] == 'O' && data[2] == 'O' && data[3] == 'M'
&& data[6] >= 3 && xadCRC_1021_2(data, sizeof(struct ZoomHeader)) ==
EndGetM16(data+72))
return 1;
else
return 0;
}
/**************************************************************************/
XADGETINFO(Zoom)
{
struct xadDiskInfo *di;
xadINT32 ret = XADERR_NOMEMORY;
xadINT32 oldmode = 0;
if((di = (struct xadDiskInfo *) xadAllocObject(XADM XADOBJ_DISKINFO,
XAD_OBJPRIVINFOSIZE, sizeof(struct ZoomHeader), TAG_DONE)))
{
struct ZoomHeader *zh;
ai->xai_DiskInfo = di;
zh = (struct ZoomHeader *) di->xdi_PrivateInfo;
if(!(ret = xadHookAccess(XADM XADAC_READ, sizeof(struct ZoomHeader), zh, ai)))
{
if(zh->HeaderID == 0x5A4F4F4D)
oldmode = 1;
if(!(ret = CheckCRC(zh, sizeof(struct ZoomHeader), oldmode, 0, ai,
xadMasterBase)))
{
if(zh->Encrypted)
{
di->xdi_Flags |= XADDIF_CRYPTED;
ai->xai_Flags |= XADAIF_CRYPTED;
}
di->xdi_EntryNumber = 1;
di->xdi_SectorSize = 512;
di->xdi_Cylinders = 80;
di->xdi_Heads = 2;
di->xdi_TrackSectors = 11;
di->xdi_CylSectors = 22;
di->xdi_TotalSectors = 80 * 22;
di->xdi_LowCyl = 0;
di->xdi_HighCyl = 79;
if(zh->TextLength)
{
struct xadTextInfo *ti;
if((ti = (struct xadTextInfo *) xadAllocObjectA(XADM XADOBJ_TEXTINFO, 0)))
{
ti->xti_Size = zh->TextLength;
if((ret = DoDecrunch(&ti->xti_Text, zh->PackedLength, zh->TextLength,
oldmode, ai, xadMasterBase, 0)))
xadFreeObjectA(XADM ti, 0);
else
di->xdi_TextInfo = ti;
}
else
ret = XADERR_NOMEMORY;
}
di->xdi_DataPos = ai->xai_InPos;
if(zh->Flags & USEHEADER)
di->xdi_Flags |= XADDIF_SECTORLABELS;
di->xdi_Flags |= XADDIF_SEEKDATAPOS;
if(zh->NoteOffset)
{
struct ZoomNote zn;
if(xadHookAccess(XADM XADAC_INPUTSEEK, zh->NoteOffset - ai->xai_InPos, 0, ai))
ai->xai_Flags |= XADAIF_FILECORRUPT;
else if(!(ret = xadHookAccess(XADM XADAC_READ, sizeof(struct ZoomNote), &zn, ai)))
{
struct xadTextInfo *ti;
if((ti = (struct xadTextInfo *) xadAllocObjectA(XADM XADOBJ_TEXTINFO, 0)))
{
xadSTRPTR buf;
if(di->xdi_TextInfo)
di->xdi_TextInfo->xti_Next = ti;
else
di->xdi_TextInfo = ti;
if((buf = (xadSTRPTR) xadAllocVec(XADM 80+22, XADMEMF_CLEAR|XADMEMF_PUBLIC)))
{
struct xadDate xd;
xadSTRPTR Block;
xadINT32 Tmp, Length, root;
Block = (xadSTRPTR) zn.Note;
Length = 80;
root = EndGetM32(zn.DateStamp+8);
while(Length-- > 0)
{
root = (((root * EndGetM32(zn.DateStamp+4)) & 0xFFFFFF)
+ EndGetM32(zn.DateStamp)) & 0xFFFFFF;
Tmp = root % 256;
*Block++ ^= Tmp;
}
ti->xti_Text = buf;
ti->xti_Size = 80+21;
xadConvertDates(XADM XAD_DATEDATESTAMP, &zn.DateStamp,
XAD_GETDATEXADDATE, &xd, TAG_DONE);
sprintf((char *)buf, "%02d.%02d.%04d %02d:%02d:%02d\n\n",
(xadUINT32)xd.xd_Day, (xadUINT32)xd.xd_Month,
(xadUINT32)xd.xd_Year, (xadUINT32)xd.xd_Hour,
(xadUINT32)xd.xd_Minute, (xadUINT32)xd.xd_Second);
xadCopyMem(XADM (xadPTR) &zn.Note, buf+21, 80);
}
else
ret = XADERR_NOMEMORY;
}
else
ret = XADERR_NOMEMORY;
} /* xadHokkAccess */
} /* zh->NoteOffset */
} /* CheckCRC */
} /* XADAC_READ */
} /* xadAllocObject */
return ret;
}
static xadINT32 GetTrackHead(struct xadArchiveInfo *ai, struct TrackHeader *th,
xadUINT32 oldmode, xadUINT32 useheader, struct xadMasterBase *xadMasterBase)
{
xadINT32 err;
if(!(err = xadHookAccess(XADM XADAC_READ, sizeof(struct TrackHeader)-2, th, ai)))
{
if(!(err = CheckCRC(th, sizeof(struct TrackHeader)-2, oldmode, 0, ai, xadMasterBase)))
{
xadUINT32 i, j = 0, k, l;
if(oldmode)
{
th->Bytes = th->SqueezedLength;
th->SqueezedLength = th->FatLength;
// th->FatLength = th->Packed;
th->Packed = th->CRC >> 16;
th->CRC = th->CRC & 0xFFFF;
}
/* as Zoom sets wrong FatLength sometimes, we need to recalculate it! */
for(i = 0; i < BOXSIZE && th->Tracks[i] != -1; ++i)
{
l = th->Bits[i];
for(k = 0; k < 22; ++k)
{
if(l & 1)
++j;
l >>= 1;
}
}
th->FatLength = j * (useheader ? 512 + 16 : 512);
}
}
return err;
}
XADUNARCHIVE(Zoom)
{
struct xadDiskInfo *di;
struct ZoomHeader *zh;
struct TrackHeader th;
xadINT32 err = 0, i, j = -1, readdata = 0, oldmode;
xadSTRPTR pwd = 0, buf = 0, ebuf;
di = ai->xai_CurDisk;
zh = (struct ZoomHeader *) di->xdi_PrivateInfo;
oldmode = (zh->HeaderID == 0x5A4F4F4D);
if(di->xdi_Flags & XADDIF_CRYPTED)
{
if(!ai->xai_Password || NewKey(ai->xai_Password) != zh->root)
return XADERR_PASSWORD;
else
pwd = ai->xai_Password;
}
if(!(ebuf = xadAllocVec(XADM TRACKSIZE, XADMEMF_PUBLIC)))
return XADERR_NOMEMORY;
for(i = ai->xai_LowCyl; !err && i <= ai->xai_HighCyl; ++i)
{
if(j >= 0 || !(err = GetTrackHead(ai, &th, oldmode, zh->Flags & USEHEADER, xadMasterBase)))
{
if(j == -1)
j = 0;
if(j > BOXSIZE || th.Tracks[j] > i)
{
ClearMemLong((xadUINT32 *) ebuf, TRACKSIZE/4);
err = xadHookAccess(XADM XADAC_WRITE, TRACKSIZE, ebuf, ai);
}
else
{
if(th.Tracks[j] == i)
{
if(!readdata)
{
if(!(err = DoDecrunchZoom(&buf, &th, oldmode, pwd, ai, xadMasterBase)))
readdata = 1;
}
if(readdata)
{
if(zh->Flags & USEHEADER)
err = xadHookTagAccess(XADM XADAC_WRITE, TRACKSIZE,
buf+(j * (TRACKSIZE + 22*16)), ai, XAD_SECTORLABELS,
buf+(j * (TRACKSIZE + 22*16)) + TRACKSIZE, TAG_DONE);
else
{
UnPackTrack(&th, buf, ebuf, j, xadMasterBase);
err = xadHookAccess(XADM XADAC_WRITE, TRACKSIZE, ebuf, ai);
}
}
}
else
--i; /* was a back-track --> skipped, run this loop again */
if(++j == BOXSIZE)
{
if(i < zh->To) /* if not, j runs over --> th is no longer accessable */
{
if(!readdata)
err = xadHookAccess(XADM XADAC_INPUTSEEK, th.Length, 0, ai);
else
xadFreeObjectA(XADM buf, 0);
j = -1; readdata = 0; buf = 0;
}
}
} /* Tracks <= i */
} /* read TrackHeader */
} /* for loop */
if(buf)
xadFreeObjectA(XADM buf, 0);
xadFreeObjectA(XADM ebuf, 0);
return err;
}
/**************************************************************************/
struct LhPakEntry {
xadUINT32 SkipSize; /* 0 */
xadUINT16 CrunchedSize; /* 4 */
xadUINT16 BlockNumber; /* 6 */
xadUINT32 UnCrunchedSize; /* 8 */
xadUINT32 CRC; /* C */
xadUINT32 CheckSum; /* 10 */
};
struct LhPakHead {
struct LhPakEntry Entry; /* 0 */
xadUINT32 Protection; /* 14 */
xadUINT32 Size; /* 18 */
xadUINT8 Date[12]; /* 1C */
xadUINT16 pad1; /* 28 */
xadUINT8 LengthFileName; /* 2A */
xadUINT8 LengthComment; /* 2B */
};
XADRECOGDATA(LhPak)
{
if(((xadUINT32 *)data)[0] == 0x3F3 /* HUNK_HEADER */)
{
if(((xadUINT32 *)data)[10] == 0x4C534658 &&
((xadUINT32 *)data)[12] == 0x2C790000 &&
((xadUINT32 *)data)[14] == 0x01144AAA &&
((xadUINT32 *)data)[15] == 0x00AC6600 &&
((xadUINT32 *)data)[16] == 0x001841EA)
{
return 1;
}
}
return 0;
}
/* maybe there are some errors in that code, not tested yet */
XADGETINFO(LhPak)
{
xadINT32 err;
xadUINT32 buffer[6];
if(!(err = xadHookAccess(XADM XADAC_READ, 24, buffer, ai)))
{
if(!(err = xadHookAccess(XADM XADAC_INPUTSEEK, 0x80+buffer[5]*4-24, 0, ai)))
{
struct xadFileInfo *fi = 0, *fi2;
struct LhPakHead ph;
xadINT32 goon = 1;
xadUINT32 pos, num = 1;
while(goon && !err)
{
pos = (xadUINT32)ai->xai_InPos;
if(!(err = xadHookAccess(XADM XADAC_READ, sizeof(struct LhPakHead), &ph, ai)))
{
if(!ph.Entry.SkipSize)
goon = 0;
if(!(fi2 = xadAllocObject(XADM XADOBJ_FILEINFO, XAD_OBJNAMESIZE,
4*ph.LengthFileName, ph.LengthComment ? XAD_OBJCOMMENTSIZE : TAG_DONE,
4*ph.LengthComment, TAG_DONE)))
err = XADERR_NOMEMORY;
else
{
fi2->xfi_EntryNumber = num++;
fi2->xfi_DataPos = pos;
fi2->xfi_Flags |= XADFIF_SEEKDATAPOS;
fi2->xfi_CrunchSize = ph.Entry.CrunchedSize;
fi2->xfi_Protection = ph.Protection;
xadConvertDates(XADM XAD_DATEDATESTAMP, &ph.Date, XAD_GETDATEXADDATE,
&fi2->xfi_Date, TAG_DONE);
fi2->xfi_Size = ph.Size;
if(!(err = xadHookAccess(XADM XADAC_READ, 4*ph.LengthFileName, fi2->xfi_FileName, ai)))
{
if(!ph.LengthComment || !(err = xadHookAccess(XADM XADAC_READ, 4*ph.LengthComment, fi2->xfi_Comment, ai)))
{
xadUINT32 i = 0, j;
for(pos = 0; pos < 11; ++pos)
i += ((xadUINT32 *) &ph)[pos];
for(pos = 0; pos < ph.LengthFileName; ++pos)
i += ((xadUINT32 *) fi2->xfi_FileName)[pos];
for(pos = 0; pos < ph.LengthComment; ++pos)
i += ((xadUINT32 *) fi2->xfi_Comment)[pos];
if(i)
err = XADERR_CHECKSUM;
else if(goon && !(err = xadHookAccess(XADM XADAC_INPUTSEEK, ph.Entry.SkipSize-
sizeof(struct LhPakHead)-4*(ph.LengthFileName+ph.LengthComment), 0, ai)))
{
i = ph.Entry.UnCrunchedSize;
while(i < ph.Size && !err && goon)
{
if(!(err = xadHookAccess(XADM XADAC_READ, sizeof(struct LhPakEntry), &ph, ai)))
{
for(j = pos = 0; pos < 5; ++pos)
j += ((xadUINT32 *) &ph)[pos];
if(j)
err = XADERR_CHECKSUM;
else
{
i += ph.Entry.UnCrunchedSize;
fi2->xfi_CrunchSize += ph.Entry.CrunchedSize;
if(!ph.Entry.SkipSize)
goon = 0;
else
err = xadHookAccess(XADM XADAC_INPUTSEEK, ph.Entry.SkipSize-sizeof(struct LhPakEntry), 0, ai);
}
}
}
if(!goon && i < ph.Size)
err = XADERR_ILLEGALDATA;
}
}
}
if(err)
xadFreeObjectA(XADM fi2, 0);
else if(fi)
{
fi->xfi_Next = fi2; fi = fi2;
}
else
ai->xai_FileInfo = fi = fi2;
}
}
}
if(err && fi)
{
err = 0;
ai->xai_Flags |= XADAIF_FILECORRUPT;
}
}
}
return err;
}
XADUNARCHIVE(LhPak)
{
xadINT32 i, j, err;
struct LhPakHead ph;
xadSTRPTR buf;
if(!(err = xadHookAccess(XADM XADAC_READ, sizeof(struct LhPakHead), &ph, ai)))
{
if(!(err = xadHookAccess(XADM XADAC_INPUTSEEK, (ph.LengthFileName+ph.LengthComment)*4, 0, ai)))
{
i = 0;
ph.Entry.SkipSize -= sizeof(struct LhPakHead) - sizeof(struct LhPakEntry) +
(ph.LengthFileName+ph.LengthComment)*4;
while(i < ph.Size && !err)
{
i += ph.Entry.UnCrunchedSize;
if(!(err = DoDecrunch(&buf, ph.Entry.CrunchedSize, ph.Entry.UnCrunchedSize,
100, ai, xadMasterBase, ph.Entry.CRC)))
{
err = xadHookAccess(XADM XADAC_WRITE, ph.Entry.UnCrunchedSize, buf, ai);
xadFreeObjectA(XADM buf,0);
}
if(!err && i < ph.Size)
{
if((j = ph.Entry.SkipSize - ph.Entry.CrunchedSize - sizeof(struct LhPakEntry)))
err = xadHookAccess(XADM XADAC_INPUTSEEK, j, 0, ai);
if(!err)
err = xadHookAccess(XADM XADAC_READ, sizeof(struct LhPakEntry), &ph, ai);
}
}
}
}
return err;
}
/**************************************************************************/
XADRECOGDATA(PCompPACK)
{
if(*((xadUINT32 *) data) == 0x5041434B)
{
xadUINT32 i;
for(i = 4; i < size; ++i)
{
if(data[i] == '\n')
{
if(++i < size && data[i]) /* too large file size */
return 0;
return 1;
}
else if((data[i] & 0x7F) < 32) /* printable ASCII name */
return 0;
}
return 1;
}
return 0;
}
XADGETINFO(PCompPACK)
{
xadINT32 err, i, j, k, num = 1;
xadUINT8 buffer[512];
struct xadFileInfo *fi = 0, *fi2;
if((err = xadHookAccess(XADM XADAC_INPUTSEEK, 4, 0, ai)))
return err;
while(!err && ai->xai_InSize-ai->xai_InPos > 4)
{
if((j = (xadINT32)(ai->xai_InSize-ai->xai_InPos)) > 512)
j = 512;
if(!(err = xadHookAccess(XADM XADAC_READ, j, buffer, ai)))
{
for(i = 0; i != j - 5 && buffer[i] != '\n'; ++i)
;
if(buffer[i] == '\n')
{
k = (buffer[i+1]<<24)+(buffer[i+2]<<16)+(buffer[i+3]<<8)+buffer[i+4];
j = (xadINT32)ai->xai_InPos-j+i+5;
if(!(err = xadHookAccess(XADM XADAC_INPUTSEEK, j-ai->xai_InPos+k, 0, ai)))
{
if((fi2 = (struct xadFileInfo *) xadAllocObject(XADM XADOBJ_FILEINFO,
XAD_OBJNAMESIZE, i+1, TAG_DONE)))
{
fi2->xfi_DataPos = j; /* file position */
fi2->xfi_CrunchSize = k;
fi2->xfi_EntryNumber = num++;
fi2->xfi_Flags = XADFIF_NODATE|XADFIF_SEEKDATAPOS;
for(j = 0; j < i; ++j)
fi2->xfi_FileName[j] = buffer[j];
err = xadConvertDates(XADM XAD_DATECURRENTTIME, 1, XAD_GETDATEXADDATE,
&fi2->xfi_Date, TAG_DONE);
if(k > 10)
{
i += 5;
if(buffer[i] == 'L' && buffer[i+1] == 'H' && buffer[i+2] == 0 && buffer[i+6] == 0)
k = (buffer[i+2]<<24)+(buffer[i+3]<<16)+(buffer[i+4]<<8)+buffer[i+5];
}
fi2->xfi_Size = k;
if(fi)
fi->xfi_Next = fi2;
else
ai->xai_FileInfo = fi2;
fi = fi2;
}
else
err = XADERR_NOMEMORY;
}
}
else
err = XADERR_ILLEGALDATA;
}
}
if(err)
{
ai->xai_Flags |= XADAIF_FILECORRUPT;
ai->xai_LastError = err;
}
return fi ? 0 : XADERR_ILLEGALDATA;
}
struct PCompData {
xadUINT16 ID;
xadUINT32 Size;
xadUINT32 CrunchedSize;
xadUINT32 CRC;
};
XADUNARCHIVE(PCompPACK)
{
xadINT32 i, err;
i = (xadINT32)ai->xai_CurFile->xfi_Size;
if(i == ai->xai_CurFile->xfi_CrunchSize)
err = xadHookAccess(XADM XADAC_COPY, i, 0, ai);
else
{
struct PCompData pc;
xadSTRPTR buf;
if(!(err = xadHookAccess(XADM XADAC_READ, sizeof(struct PCompData), &pc, ai)))
{
if(!(err = DoDecrunch(&buf, pc.CrunchedSize, pc.Size, 100, ai, xadMasterBase,
pc.CRC)))
{
err = xadHookAccess(XADM XADAC_WRITE, pc.Size, buf, ai);
xadFreeObjectA(XADM buf, 0);
}
}
}
return err;
}
/**************************************************************************/
XADRECOGDATA(SOmni)
{
if(((xadUINT32 *)data)[0] == 0x3F3 /* HUNK_HEADER */)
{
if(((xadUINT32 *)data)[10] == 0x42ADFFF4 &&
((xadUINT32 *)data)[12] == 0x00044EAE &&
((xadUINT32 *)data)[14] == 0x4AAC00AC &&
((xadUINT32 *)data)[15] == 0x67026014 &&
((xadUINT32 *)data)[16] == 0x41EC005C)
{
return 1;
}
}
return 0;
}
XADGETINFO(SOmni)
{
xadINT32 err, i, j, num = 1;
xadUINT16 a;
xadUINT8 buffer[10];
struct xadFileInfo *fi = 0, *fi2;
if((err = xadHookAccess(XADM XADAC_INPUTSEEK, 0x208, 0, ai)))
return err;
if((err = xadHookAccess(XADM XADAC_READ, 4, &i, ai)))
return err;
if(i == 0x51CDFFFC)
{
if((err = xadHookAccess(XADM XADAC_INPUTSEEK, 0x7A8-0x208-4, 0, ai)))
return err;
}
else if(i == 0x76002C7A)
{
if((err = xadHookAccess(XADM XADAC_INPUTSEEK, 0x744-0x208-4, 0, ai)))
return err;
}
else
return XADERR_ILLEGALDATA;
if((err = xadHookAccess(XADM XADAC_READ, 2, &a, ai)))
return err;
if(a == 1) /* skip command line */
{
if((err = xadHookAccess(XADM XADAC_READ, 2, &a, ai)))
return err;
if((err = xadHookAccess(XADM XADAC_INPUTSEEK, a, 0, ai)))
return err;
}
while(!err)
{
if(!(err = xadHookAccess(XADM XADAC_READ, 2, &a, ai)) && a)
{
if((fi2 = (struct xadFileInfo *) xadAllocObject(XADM XADOBJ_FILEINFO,
XAD_OBJNAMESIZE, a+1, TAG_DONE)))
{
if(!(err = xadHookAccess(XADM XADAC_READ, a, fi2->xfi_FileName, ai)))
{
if(!(err = xadHookAccess(XADM XADAC_READ, 4, &i, ai)))
{
fi2->xfi_DataPos = ai->xai_InPos; /* file position */
fi2->xfi_CrunchSize = i;
j = i+2;
fi2->xfi_EntryNumber = num++;
fi2->xfi_Flags = XADFIF_NODATE|XADFIF_SEEKDATAPOS;
err = xadConvertDates(XADM XAD_DATECURRENTTIME, 1, XAD_GETDATEXADDATE,
&fi2->xfi_Date, TAG_DONE);
if(i > 10)
{
if(!(err = xadHookAccess(XADM XADAC_READ, 10, buffer, ai)))
{
if(buffer[0] == 'L' && buffer[1] == 'H' && buffer[2] == 0 && buffer[6] == 0)
i = (buffer[2]<<24)+(buffer[3]<<16)+(buffer[4]<<8)+buffer[5];
j -= 10;
}
}
fi2->xfi_Size = i;
if(!err)
err = xadHookAccess(XADM XADAC_INPUTSEEK, j, 0, ai);
}
}
if(err)
xadFreeObjectA(XADM fi2, 0);
else
{
if(fi)
fi->xfi_Next = fi2;
else
ai->xai_FileInfo = fi2;
fi = fi2;
}
}
else
err = XADERR_NOMEMORY;
}
else if(!a)
break;
}
if(err)
{
ai->xai_Flags |= XADAIF_FILECORRUPT;
ai->xai_LastError = err;
}
return fi ? 0 : XADERR_ILLEGALDATA;
}
/**************************************************************************/
XADRECOGDATA(LhSFX)
{
if(((xadUINT32 *)data)[0] == 0x3F3 /* HUNK_HEADER */)
{
if(((xadUINT32 *)data)[10] == 0x43F90000 &&
((xadUINT32 *)data)[12] == 0x00000318 &&
((xadUINT32 *)data)[14] == 0x02EC2C79 &&
((xadUINT32 *)data)[15] == 0x00000004 &&
((xadUINT32 *)data)[16] == 0x4EAEFDD8)
{
return 1;
}
}
return 0;
}
struct LhSFXData {
xadUINT32 Size;
xadUINT32 CrSize;
xadUINT32 pad; /* don't know what that is! */
xadUINT8 Name[48];
};
XADGETINFO(LhSFX)
{
xadINT32 err, i, j, num = 1;
struct LhSFXData sf;
struct xadFileInfo *fi = 0, *fi2;
if((err = xadHookAccess(XADM XADAC_INPUTSEEK, 0x918, 0, ai)))
return err;
while(!err)
{
if(!(err = xadHookAccess(XADM XADAC_READ, 4, &i, ai)) && i != -1)
{
if(!(err = xadHookAccess(XADM XADAC_READ, sizeof(struct LhSFXData), &sf, ai)))
{
j = (xadINT32)ai->xai_InPos;
if(!(err = xadHookAccess(XADM XADAC_INPUTSEEK, sf.CrSize, 0, ai)))
{
for(i = 0; i < 48 && sf.Name[i]; ++i)
;
if((fi2 = (struct xadFileInfo *) xadAllocObject(XADM XADOBJ_FILEINFO,
i ? XAD_OBJNAMESIZE : TAG_IGNORE, i+1, TAG_DONE)))
{
if(i)
xadCopyMem(XADM sf.Name, fi2->xfi_FileName, i);
else
{
fi2->xfi_FileName = xadGetDefaultName(XADM XAD_ARCHIVEINFO, ai,
XAD_EXTENSION, ".sfx", TAG_DONE);
fi2->xfi_Flags |= XADFIF_NOFILENAME|XADFIF_XADSTRFILENAME;
}
fi2->xfi_DataPos = j; /* file position */
fi2->xfi_CrunchSize = sf.CrSize;
fi2->xfi_Size = sf.Size;
fi2->xfi_EntryNumber = num++;
fi2->xfi_Flags |= XADFIF_NODATE|XADFIF_SEEKDATAPOS;
err = xadConvertDates(XADM XAD_DATECURRENTTIME, 1, XAD_GETDATEXADDATE,
&fi2->xfi_Date, TAG_DONE);
if(fi)
fi->xfi_Next = fi2;
else
ai->xai_FileInfo = fi2;
fi = fi2;
}
else
err = XADERR_NOMEMORY;
}
}
}
else if(i == -1)
break;
}
if(err)
{
ai->xai_Flags |= XADAIF_FILECORRUPT;
ai->xai_LastError = err;
}
return fi ? 0 : XADERR_ILLEGALDATA;
}
XADUNARCHIVE(LhSFX)
{
xadSTRPTR buf;
xadINT32 err;
if(!(err = DoDecrunch(&buf, (xadUINT32)ai->xai_CurFile->xfi_CrunchSize,
(xadUINT32)ai->xai_CurFile->xfi_Size, 200, ai, xadMasterBase, 0)))
{
err = xadHookAccess(XADM XADAC_WRITE, ai->xai_CurFile->xfi_Size, buf, ai);
xadFreeObjectA(XADM buf, 0);
}
return err;
}
XADCLIENT(LhSFX) {
XADNEXTCLIENT,
XADCLIENT_VERSION,
XADMASTERVERSION,
SOMNI_VERSION,
SOMNI_REVISION,
100,
XADCF_FILEARCHIVER|XADCF_FREEFILEINFO,
XADCID_LHSFX,
"LhSFX",
XADRECOGDATAP(LhSFX),
XADGETINFOP(LhSFX),
XADUNARCHIVEP(LhSFX),
NULL
};
XADCLIENT(SOmni) {
(struct xadClient *) &LhSFX_Client,
XADCLIENT_VERSION,
XADMASTERVERSION,
SOMNI_VERSION,
SOMNI_REVISION,
100,
XADCF_FILEARCHIVER|XADCF_FREEFILEINFO,
XADCID_SOMNI,
"S-Omni",
XADRECOGDATAP(SOmni),
XADGETINFOP(SOmni),
XADUNARCHIVEP(PCompPACK),
NULL
};
XADCLIENT(PCompPACK) {
(struct xadClient *) &SOmni_Client,
XADCLIENT_VERSION,
XADMASTERVERSION,
PCOMP_VERSION,
PCOMP_REVISION,
50,
XADCF_FILEARCHIVER|XADCF_FREEFILEINFO,
XADCID_PCOMPARC,
"PCompress PACK",
XADRECOGDATAP(PCompPACK),
XADGETINFOP(PCompPACK),
XADUNARCHIVEP(PCompPACK),
NULL
};
XADCLIENT(LhPak) {
(struct xadClient *) &PCompPACK_Client,
XADCLIENT_VERSION,
XADMASTERVERSION,
LHPAK_VERSION,
LHPAK_REVISION,
100,
XADCF_FILEARCHIVER|XADCF_FREEFILEINFO,
XADCID_LHPAK,
"LhPak SFX",
XADRECOGDATAP(LhPak),
XADGETINFOP(LhPak),
XADUNARCHIVEP(LhPak),
NULL
};
XADCLIENT(Zoom5) {
(struct xadClient *) &LhPak_Client,
XADCLIENT_VERSION,
XADMASTERVERSION,
ZOOM_VERSION,
ZOOM_REVISION,
76,
XADCF_DISKARCHIVER|XADCF_FREEDISKINFO|XADCF_FREETEXTINFO|XADCF_FREETEXTINFOTEXT,
XADCID_ZOOM5,
"Zoom 5",
XADRECOGDATAP(Zoom5),
XADGETINFOP(Zoom),
XADUNARCHIVEP(Zoom),
NULL
};
XADFIRSTCLIENT(Zoom) {
(struct xadClient *) &Zoom5_Client,
XADCLIENT_VERSION,
XADMASTERVERSION,
ZOOM_VERSION,
ZOOM_REVISION,
76,
XADCF_DISKARCHIVER|XADCF_FREEDISKINFO|XADCF_FREETEXTINFO|XADCF_FREETEXTINFOTEXT,
XADCID_ZOOM,
"Zoom",
XADRECOGDATAP(Zoom),
XADGETINFOP(Zoom),
XADUNARCHIVEP(Zoom),
NULL
};
#undef XADNEXTCLIENT
#define XADNEXTCLIENT XADNEXTCLIENTNAME(Zoom)
#endif /* XADMASTER_ZOOM_C */