2148 lines
50 KiB
C++
2148 lines
50 KiB
C++
/*
|
|
* UAE - The Un*x Amiga Emulator
|
|
*
|
|
* transparent archive handling
|
|
*
|
|
* 2007 Toni Wilen
|
|
*/
|
|
#include <string.h>
|
|
#include <time.h>
|
|
#include <strings.h>
|
|
#include <stdio.h>
|
|
|
|
#include "sysdeps.h"
|
|
|
|
#ifdef _WIN32
|
|
#include <windows.h>
|
|
#include "win32.h"
|
|
#endif
|
|
|
|
#include "zfile.h"
|
|
#include "archivers/zip/unzip.h"
|
|
#include "zarchive.h"
|
|
#include "disk.h"
|
|
|
|
|
|
static time_t fromdostime (uae_u32 dd)
|
|
{
|
|
struct tm tm;
|
|
time_t t;
|
|
|
|
memset (&tm, 0, sizeof tm);
|
|
tm.tm_hour = (dd >> 11) & 0x1f;
|
|
tm.tm_min = (dd >> 5) & 0x3f;
|
|
tm.tm_sec = ((dd >> 0) & 0x1f) * 2;
|
|
tm.tm_year = ((dd >> 25) & 0x7f) + 80;
|
|
tm.tm_mon = ((dd >> 21) & 0x0f) - 1;
|
|
tm.tm_mday = (dd >> 16) & 0x1f;
|
|
t = mktime (&tm);
|
|
t -= _timezone;
|
|
return t;
|
|
}
|
|
|
|
static struct zvolume *getzvolume (struct znode *parent, struct zfile *zf, unsigned int id)
|
|
{
|
|
struct zvolume *zv = NULL;
|
|
|
|
switch (id)
|
|
{
|
|
#ifdef A_ZIP
|
|
case ArchiveFormatZIP:
|
|
zv = archive_directory_zip (zf);
|
|
break;
|
|
#endif
|
|
#ifdef A_7Z
|
|
case ArchiveFormat7Zip:
|
|
zv = archive_directory_7z (zf);
|
|
break;
|
|
#endif
|
|
#ifdef A_RAR
|
|
case ArchiveFormatRAR:
|
|
zv = archive_directory_rar (zf);
|
|
break;
|
|
#endif
|
|
#ifdef A_LHA
|
|
case ArchiveFormatLHA:
|
|
zv = archive_directory_lha (zf);
|
|
break;
|
|
#endif
|
|
#ifdef A_LZX
|
|
case ArchiveFormatLZX:
|
|
zv = archive_directory_lzx (zf);
|
|
break;
|
|
#endif
|
|
case ArchiveFormatPLAIN:
|
|
zv = archive_directory_plain (zf);
|
|
break;
|
|
case ArchiveFormatADF:
|
|
zv = archive_directory_adf (parent, zf);
|
|
break;
|
|
case ArchiveFormatRDB:
|
|
zv = archive_directory_rdb (zf);
|
|
break;
|
|
case ArchiveFormatTAR:
|
|
zv = archive_directory_tar (zf);
|
|
break;
|
|
case ArchiveFormatFAT:
|
|
zv = archive_directory_fat (zf);
|
|
break;
|
|
}
|
|
#ifdef ARCHIVEACCESS
|
|
if (!zv)
|
|
zv = archive_directory_arcacc (zf, id);
|
|
#endif
|
|
return zv;
|
|
}
|
|
|
|
struct zfile *archive_access_select (struct znode *parent, struct zfile *zf, unsigned int id, int dodefault, int *retcode, int index)
|
|
{
|
|
struct zvolume *zv;
|
|
struct znode *zn;
|
|
int zipcnt, first, select;
|
|
TCHAR tmphist[MAX_DPATH];
|
|
struct zfile *z = NULL;
|
|
int we_have_file;
|
|
int diskimg;
|
|
int mask = zf->zfdmask;
|
|
int canhistory = (mask & ZFD_DISKHISTORY) && !(mask & ZFD_CHECKONLY);
|
|
int getflag = (mask & ZFD_DELAYEDOPEN) ? FILE_DELAYEDOPEN : 0;
|
|
|
|
if (retcode)
|
|
*retcode = 0;
|
|
if (index > 0)
|
|
return NULL;
|
|
if (zfile_needwrite (zf)) {
|
|
if (retcode)
|
|
*retcode = -1;
|
|
return NULL;
|
|
}
|
|
zv = getzvolume (parent, zf, id);
|
|
if (!zv)
|
|
return NULL;
|
|
we_have_file = 0;
|
|
tmphist[0] = 0;
|
|
zipcnt = 1;
|
|
first = 1;
|
|
zn = &zv->root;
|
|
while (zn) {
|
|
int isok = 1;
|
|
|
|
diskimg = -1;
|
|
if (zn->type != ZNODE_FILE)
|
|
isok = 0;
|
|
if (zfile_is_ignore_ext (zn->fullname))
|
|
isok = 0;
|
|
diskimg = zfile_is_diskimage (zn->fullname);
|
|
if (isok) {
|
|
if (tmphist[0]) {
|
|
tmphist[0] = 0;
|
|
first = 0;
|
|
}
|
|
if (first) {
|
|
if (diskimg >= 0)
|
|
_tcscpy (tmphist, zn->fullname);
|
|
} else {
|
|
_tcscpy (tmphist, zn->fullname);
|
|
tmphist[0] = 0;
|
|
}
|
|
select = 0;
|
|
if (!zf->zipname)
|
|
select = 1;
|
|
if (zf->zipname && _tcslen (zn->fullname) >= _tcslen (zf->zipname) && !stricmp(zf->zipname, zn->fullname + _tcslen (zn->fullname) - _tcslen (zf->zipname)))
|
|
select = -1;
|
|
if (zf->zipname && zf->zipname[0] == '#' && _tstol (zf->zipname + 1) == zipcnt)
|
|
select = -1;
|
|
if (select && we_have_file < 10) {
|
|
struct zfile *zt = NULL;
|
|
TCHAR *ext = _tcsrchr (zn->fullname, '.');
|
|
int whf = 1;
|
|
int ft = 0;
|
|
if (mask & ZFD_CD) {
|
|
if (ext && !_tcsicmp (ext, _T(".iso"))) {
|
|
whf = 2;
|
|
ft = ZFILE_CDIMAGE;
|
|
}
|
|
if (ext && !_tcsicmp (ext, _T(".chd"))) {
|
|
whf = 2;
|
|
ft = ZFILE_CDIMAGE;
|
|
}
|
|
if (ext && !_tcsicmp (ext, _T(".ccd"))) {
|
|
whf = 9;
|
|
ft = ZFILE_CDIMAGE;
|
|
}
|
|
if (ext && !_tcsicmp (ext, _T(".cue"))) {
|
|
whf = 10;
|
|
ft = ZFILE_CDIMAGE;
|
|
}
|
|
} else {
|
|
zt = archive_getzfile (zn, id, getflag);
|
|
ft = zfile_gettype (zt);
|
|
}
|
|
if ((select < 0 || ft) && whf > we_have_file) {
|
|
if (!zt)
|
|
zt = archive_getzfile (zn, id, getflag);
|
|
we_have_file = whf;
|
|
if (z)
|
|
zfile_fclose (z);
|
|
z = zt;
|
|
zt = NULL;
|
|
}
|
|
zfile_fclose (zt);
|
|
}
|
|
}
|
|
zipcnt++;
|
|
zn = zn->next;
|
|
}
|
|
#ifndef _CONSOLE
|
|
diskimg = zfile_is_diskimage (zfile_getname (zf));
|
|
#endif
|
|
zfile_fclose_archive (zv);
|
|
if (z) {
|
|
zfile_fclose (zf);
|
|
zf = z;
|
|
} else if (!dodefault && zf->zipname && zf->zipname[0]) {
|
|
if (retcode)
|
|
*retcode = -1;
|
|
zf = NULL;
|
|
} else {
|
|
zf = NULL;
|
|
}
|
|
return zf;
|
|
}
|
|
|
|
struct zfile *archive_access_arcacc_select (struct zfile *zf, unsigned int id, int *retcode)
|
|
{
|
|
if (zfile_needwrite (zf)) {
|
|
if (retcode)
|
|
*retcode = -1;
|
|
return NULL;
|
|
}
|
|
return zf;
|
|
}
|
|
|
|
void archive_access_scan (struct zfile *zf, zfile_callback zc, void *user, unsigned int id)
|
|
{
|
|
struct zvolume *zv;
|
|
struct znode *zn;
|
|
|
|
zv = getzvolume (NULL, zf, id);
|
|
if (!zv)
|
|
return;
|
|
zn = &zv->root;
|
|
while (zn) {
|
|
if (zn->type == ZNODE_FILE) {
|
|
struct zfile *zf2 = archive_getzfile (zn, id, 0);
|
|
if (zf2) {
|
|
int ztype = iszip (zf2);
|
|
if (ztype) {
|
|
zfile_fclose (zf2);
|
|
} else {
|
|
int ret = zc (zf2, user);
|
|
zfile_fclose (zf2);
|
|
if (ret)
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
zn = zn->next;
|
|
}
|
|
zfile_fclose_archive (zv);
|
|
}
|
|
|
|
/* TAR */
|
|
|
|
static void archive_close_tar (void *handle)
|
|
{
|
|
}
|
|
|
|
struct zvolume *archive_directory_tar (struct zfile *z)
|
|
{
|
|
struct zvolume *zv;
|
|
struct znode *zn;
|
|
|
|
_tzset ();
|
|
zv = zvolume_alloc (z, ArchiveFormatTAR, NULL, NULL);
|
|
for (;;) {
|
|
uae_u8 block[512];
|
|
char name[MAX_DPATH];
|
|
int ustar = 0;
|
|
struct zarchive_info zai;
|
|
int valid = 1;
|
|
uae_u64 size;
|
|
|
|
if (zfile_fread (block, 512, 1, z) != 1)
|
|
break;
|
|
if (block[0] == 0)
|
|
break;
|
|
|
|
if (!memcmp (block + 257, "ustar ", 8))
|
|
ustar = 1;
|
|
name[0] = 0;
|
|
if (ustar)
|
|
strcpy (name, (char*)block + 345);
|
|
strcat (name, (char*)block);
|
|
|
|
if (name[0] == 0)
|
|
valid = 0;
|
|
if (block[156] != '0')
|
|
valid = 0;
|
|
if (ustar && (block[256] != 0 && block[256] != '0'))
|
|
valid = 0;
|
|
|
|
size = _strtoui64 ((char*)block + 124, NULL, 8);
|
|
|
|
if (valid) {
|
|
memset (&zai, 0, sizeof zai);
|
|
zai.name = au (name);
|
|
zai.size = size;
|
|
zai.tv.tv_sec = _strtoui64 ((char*)block + 136, NULL, 8);
|
|
zai.tv.tv_sec += _timezone;
|
|
if (_daylight)
|
|
zai.tv.tv_sec -= 1 * 60 * 60;
|
|
if (zai.name[_tcslen (zai.name) - 1] == '/') {
|
|
zn = zvolume_adddir_abs (zv, &zai);
|
|
} else {
|
|
zn = zvolume_addfile_abs (zv, &zai);
|
|
if (zn)
|
|
zn->offset = zfile_ftell (z);
|
|
}
|
|
xfree (zai.name);
|
|
}
|
|
zfile_fseek (z, (size + 511) & ~511, SEEK_CUR);
|
|
}
|
|
zv->method = ArchiveFormatTAR;
|
|
return zv;
|
|
}
|
|
|
|
static struct zfile *archive_access_tar (struct znode *zn)
|
|
{
|
|
return zfile_fopen_parent (zn->volume->archive, zn->fullname, zn->offset, zn->size);
|
|
}
|
|
|
|
/* ZIP */
|
|
#ifdef A_ZIP
|
|
|
|
static void archive_close_zip (void *handle)
|
|
{
|
|
}
|
|
|
|
struct zvolume *archive_directory_zip (struct zfile *z)
|
|
{
|
|
unzFile uz;
|
|
unz_file_info file_info;
|
|
struct zvolume *zv;
|
|
int err;
|
|
|
|
uz = unzOpen (z);
|
|
if (!uz)
|
|
return 0;
|
|
if (unzGoToFirstFile (uz) != UNZ_OK)
|
|
return 0;
|
|
zv = zvolume_alloc (z, ArchiveFormatZIP, NULL, NULL);
|
|
for (;;) {
|
|
char filename_inzip2[MAX_DPATH];
|
|
TCHAR c;
|
|
struct zarchive_info zai;
|
|
time_t t;
|
|
unsigned int dd;
|
|
TCHAR *filename_inzip;
|
|
|
|
err = unzGetCurrentFileInfo (uz, &file_info, filename_inzip2, sizeof (filename_inzip2), NULL, 0, NULL, 0);
|
|
if (err != UNZ_OK)
|
|
return 0;
|
|
if (file_info.flag & (1 << 11)) { // UTF-8 encoded
|
|
filename_inzip = utf8u (filename_inzip2);
|
|
} else {
|
|
filename_inzip = au (filename_inzip2);
|
|
}
|
|
dd = file_info.dosDate;
|
|
t = fromdostime (dd);
|
|
memset (&zai, 0, sizeof zai);
|
|
zai.name = filename_inzip;
|
|
zai.tv.tv_sec = t;
|
|
zai.flags = -1;
|
|
c = filename_inzip[_tcslen (filename_inzip) - 1];
|
|
if (c != '/' && c != '\\') {
|
|
int err = unzOpenCurrentFile (uz);
|
|
if (err == UNZ_OK) {
|
|
struct znode *zn;
|
|
zai.size = file_info.uncompressed_size;
|
|
zn = zvolume_addfile_abs (zv, &zai);
|
|
}
|
|
} else {
|
|
filename_inzip[_tcslen (filename_inzip) - 1] = 0;
|
|
zvolume_adddir_abs (zv, &zai);
|
|
}
|
|
xfree (filename_inzip);
|
|
err = unzGoToNextFile (uz);
|
|
if (err != UNZ_OK)
|
|
break;
|
|
}
|
|
unzClose (uz);
|
|
zv->method = ArchiveFormatZIP;
|
|
return zv;
|
|
}
|
|
|
|
|
|
static struct zfile *archive_do_zip (struct znode *zn, struct zfile *z, int flags)
|
|
{
|
|
unzFile uz;
|
|
int i;
|
|
TCHAR tmp[MAX_DPATH];
|
|
TCHAR *name = z ? z->archiveparent->name : zn->volume->root.fullname;
|
|
char *s;
|
|
|
|
uz = unzOpen (z ? z->archiveparent : zn->volume->archive);
|
|
if (!uz)
|
|
return 0;
|
|
if (z)
|
|
_tcscpy (tmp, z->archiveparent->name);
|
|
else
|
|
_tcscpy (tmp, zn->fullname + _tcslen (zn->volume->root.fullname) + 1);
|
|
if (unzGoToFirstFile (uz) != UNZ_OK)
|
|
goto error;
|
|
for (i = 0; tmp[i]; i++) {
|
|
if (tmp[i] == '\\')
|
|
tmp[i] = '/';
|
|
}
|
|
s = ua (tmp);
|
|
if (unzLocateFile (uz, s, 1) != UNZ_OK) {
|
|
xfree (s);
|
|
for (i = 0; tmp[i]; i++) {
|
|
if (tmp[i] == '/')
|
|
tmp[i] = '\\';
|
|
}
|
|
s = ua (tmp);
|
|
if (unzLocateFile (uz, s, 1) != UNZ_OK) {
|
|
xfree (s);
|
|
goto error;
|
|
}
|
|
}
|
|
xfree (s);
|
|
s = NULL;
|
|
if (unzOpenCurrentFile (uz) != UNZ_OK)
|
|
goto error;
|
|
if (!z)
|
|
z = zfile_fopen_empty (NULL, zn->fullname, zn->size);
|
|
if (z) {
|
|
int err = -1;
|
|
if (!(flags & FILE_DELAYEDOPEN) || z->size <= PEEK_BYTES) {
|
|
err = unzReadCurrentFile (uz, z->data, z->datasize);
|
|
} else {
|
|
z->archiveparent = zfile_dup (zn->volume->archive);
|
|
if (z->archiveparent) {
|
|
xfree (z->archiveparent->name);
|
|
z->archiveparent->name = my_strdup (tmp);
|
|
z->datasize = PEEK_BYTES;
|
|
err = unzReadCurrentFile (uz, z->data, z->datasize);
|
|
} else {
|
|
err = unzReadCurrentFile (uz, z->data, z->datasize);
|
|
}
|
|
}
|
|
}
|
|
unzCloseCurrentFile (uz);
|
|
unzClose (uz);
|
|
return z;
|
|
error:
|
|
unzClose (uz);
|
|
return NULL;
|
|
}
|
|
|
|
static struct zfile *archive_access_zip (struct znode *zn, int flags)
|
|
{
|
|
return archive_do_zip (zn, NULL, flags);
|
|
}
|
|
static struct zfile *archive_unpack_zip (struct zfile *zf)
|
|
{
|
|
return archive_do_zip (NULL, zf, 0);
|
|
}
|
|
#endif
|
|
|
|
#ifdef A_7Z
|
|
/* 7Z */
|
|
|
|
#include "7z/7z.h"
|
|
#include "7z/Alloc.h"
|
|
#include "7z/7zFile.h"
|
|
#include "7z/7zVersion.h"
|
|
#include "7z/7zCrc.h"
|
|
|
|
static void *SzAlloc (void *p, size_t size)
|
|
{
|
|
return xmalloc (uae_u8, size);
|
|
}
|
|
static void SzFree(void *p, void *address)
|
|
{
|
|
xfree (address);
|
|
}
|
|
|
|
static ISzAlloc allocImp;
|
|
static ISzAlloc allocTempImp;
|
|
|
|
static SRes SzFileReadImp (void *object, void *buffer, size_t *size)
|
|
{
|
|
CFileInStream *s = (CFileInStream *)object;
|
|
#ifdef WIN32
|
|
struct zfile *zf = (struct zfile*)s->file.myhandle;
|
|
#else
|
|
struct zfile *zf = (struct zfile*)s->file.file;
|
|
#endif
|
|
*size = zfile_fread (buffer, 1, *size, zf);
|
|
return SZ_OK;
|
|
}
|
|
|
|
static SRes SzFileSeekImp(void *object, Int64 *pos, ESzSeek origin)
|
|
{
|
|
CFileInStream *s = (CFileInStream *)object;
|
|
#ifdef WIN32
|
|
struct zfile *zf = (struct zfile*)s->file.myhandle;
|
|
#else
|
|
struct zfile *zf =(struct zfile*) s->file.file;
|
|
#endif
|
|
int org = 0;
|
|
switch (origin)
|
|
{
|
|
case SZ_SEEK_SET: org = SEEK_SET; break;
|
|
case SZ_SEEK_CUR: org = SEEK_CUR; break;
|
|
case SZ_SEEK_END: org = SEEK_END; break;
|
|
}
|
|
zfile_fseek (zf, *pos, org);
|
|
*pos = zfile_ftell (zf);
|
|
return SZ_OK;
|
|
}
|
|
|
|
static void init_7z (void)
|
|
{
|
|
static int initialized;
|
|
|
|
if (initialized)
|
|
return;
|
|
initialized = 1;
|
|
allocImp.Alloc = SzAlloc;
|
|
allocImp.Free = SzFree;
|
|
allocTempImp.Alloc = SzAlloc;
|
|
allocTempImp.Free = SzFree;
|
|
CrcGenerateTable ();
|
|
_tzset ();
|
|
}
|
|
|
|
struct SevenZContext
|
|
{
|
|
CSzArEx db;
|
|
CFileInStream archiveStream;
|
|
CLookToRead lookStream;
|
|
Byte *outBuffer;
|
|
size_t outBufferSize;
|
|
UInt32 blockIndex;
|
|
};
|
|
|
|
static void archive_close_7z (void *ctx)
|
|
{
|
|
struct SevenZContext *ctx7 = (struct SevenZContext*)ctx;
|
|
SzArEx_Free (&ctx7->db, &allocImp);
|
|
allocImp.Free (&allocImp, ctx7->outBuffer);
|
|
xfree (ctx);
|
|
}
|
|
|
|
#define EPOCH_DIFF 0x019DB1DED53E8000LL /* 116444736000000000 nsecs */
|
|
#define RATE_DIFF 10000000 /* 100 nsecs */
|
|
|
|
struct zvolume *archive_directory_7z (struct zfile *z)
|
|
{
|
|
SRes res;
|
|
struct zvolume *zv;
|
|
int i;
|
|
struct SevenZContext *ctx;
|
|
|
|
init_7z ();
|
|
ctx = xcalloc (struct SevenZContext, 1);
|
|
ctx->blockIndex = 0xffffffff;
|
|
ctx->archiveStream.s.Read = SzFileReadImp;
|
|
ctx->archiveStream.s.Seek = SzFileSeekImp;
|
|
#ifdef WIN32
|
|
ctx->archiveStream.file.myhandle = (void*)z;
|
|
#else
|
|
ctx->archiveStream.file.file = (FILE*)z;
|
|
#endif
|
|
LookToRead_CreateVTable (&ctx->lookStream, False);
|
|
ctx->lookStream.realStream = &ctx->archiveStream.s;
|
|
LookToRead_Init (&ctx->lookStream);
|
|
|
|
SzArEx_Init (&ctx->db);
|
|
res = SzArEx_Open (&ctx->db, &ctx->lookStream.s, &allocImp, &allocTempImp);
|
|
if (res != SZ_OK) {
|
|
write_log (_T("7Z: SzArchiveOpen %s returned %d\n"), zfile_getname (z), res);
|
|
xfree (ctx);
|
|
return NULL;
|
|
}
|
|
zv = zvolume_alloc (z, ArchiveFormat7Zip, ctx, NULL);
|
|
for (i = 0; i < ctx->db.db.NumFiles; i++) {
|
|
CSzFileItem *f = ctx->db.db.Files + i;
|
|
TCHAR *name = (TCHAR*)(ctx->db.FileNames.data + ctx->db.FileNameOffsets[i] * 2);
|
|
struct zarchive_info zai;
|
|
|
|
memset(&zai, 0, sizeof zai);
|
|
zai.name = name;
|
|
zai.flags = f->AttribDefined ? f->Attrib : -1;
|
|
zai.size = f->Size;
|
|
if (f->MTimeDefined) {
|
|
uae_u64 t = (((uae_u64)f->MTime.High) << 32) | f->MTime.Low;
|
|
if (t >= EPOCH_DIFF) {
|
|
zai.tv.tv_sec = (t - EPOCH_DIFF) / RATE_DIFF;
|
|
zai.tv.tv_sec -= _timezone;
|
|
if (_daylight)
|
|
zai.tv.tv_sec += 1 * 60 * 60;
|
|
}
|
|
}
|
|
if (!f->IsDir) {
|
|
struct znode *zn = zvolume_addfile_abs (zv, &zai);
|
|
if (zn)
|
|
zn->offset = i;
|
|
}
|
|
}
|
|
zv->method = ArchiveFormat7Zip;
|
|
return zv;
|
|
}
|
|
|
|
static struct zfile *archive_access_7z (struct znode *zn)
|
|
{
|
|
SRes res;
|
|
struct zvolume *zv = zn->volume;
|
|
struct zfile *z = NULL;
|
|
size_t offset;
|
|
size_t outSizeProcessed;
|
|
struct SevenZContext *ctx;
|
|
|
|
z = zfile_fopen_empty (NULL, zn->fullname, zn->size);
|
|
if (!z)
|
|
return NULL;
|
|
ctx = (struct SevenZContext*)zv->handle;
|
|
res = SzArEx_Extract (&ctx->db, &ctx->lookStream.s, zn->offset,
|
|
&ctx->blockIndex, &ctx->outBuffer, &ctx->outBufferSize,
|
|
&offset, &outSizeProcessed,
|
|
&allocImp, &allocTempImp);
|
|
if (res == SZ_OK) {
|
|
zfile_fwrite (ctx->outBuffer + offset, zn->size, 1, z);
|
|
} else {
|
|
write_log (_T("7Z: SzExtract %s returned %d\n"), zn->fullname, res);
|
|
zfile_fclose (z);
|
|
z = NULL;
|
|
}
|
|
return z;
|
|
}
|
|
#endif
|
|
|
|
/* RAR */
|
|
#ifdef A_RAR
|
|
|
|
/* copy and paste job? you are only imagining it! */
|
|
static struct zfile *rarunpackzf; /* stupid unrar.dll */
|
|
#include <unrar.h>
|
|
typedef HANDLE (_stdcall* RAROPENARCHIVEEX)(struct RAROpenArchiveDataEx*);
|
|
static RAROPENARCHIVEEX pRAROpenArchiveEx;
|
|
typedef int (_stdcall* RARREADHEADEREX)(HANDLE,struct RARHeaderDataEx*);
|
|
static RARREADHEADEREX pRARReadHeaderEx;
|
|
typedef int (_stdcall* RARPROCESSFILE)(HANDLE,int,char*,char*);
|
|
static RARPROCESSFILE pRARProcessFile;
|
|
typedef int (_stdcall* RARCLOSEARCHIVE)(HANDLE);
|
|
static RARCLOSEARCHIVE pRARCloseArchive;
|
|
typedef void (_stdcall* RARSETCALLBACK)(HANDLE,UNRARCALLBACK,LONG);
|
|
static RARSETCALLBACK pRARSetCallback;
|
|
typedef int (_stdcall* RARGETDLLVERSION)(void);
|
|
static RARGETDLLVERSION pRARGetDllVersion;
|
|
|
|
static int canrar (void)
|
|
{
|
|
static int israr;
|
|
|
|
if (israr == 0) {
|
|
israr = -1;
|
|
#ifdef _WIN32
|
|
{
|
|
HMODULE rarlib;
|
|
|
|
rarlib = WIN32_LoadLibrary (_T("unrar.dll"));
|
|
if (rarlib) {
|
|
TCHAR tmp[MAX_DPATH];
|
|
tmp[0] = 0;
|
|
GetModuleFileName (rarlib, tmp, sizeof tmp / sizeof (TCHAR));
|
|
pRAROpenArchiveEx = (RAROPENARCHIVEEX)GetProcAddress (rarlib, "RAROpenArchiveEx");
|
|
pRARReadHeaderEx = (RARREADHEADEREX)GetProcAddress (rarlib, "RARReadHeaderEx");
|
|
pRARProcessFile = (RARPROCESSFILE)GetProcAddress (rarlib, "RARProcessFile");
|
|
pRARCloseArchive = (RARCLOSEARCHIVE)GetProcAddress (rarlib, "RARCloseArchive");
|
|
pRARSetCallback = (RARSETCALLBACK)GetProcAddress (rarlib, "RARSetCallback");
|
|
pRARGetDllVersion = (RARGETDLLVERSION)GetProcAddress (rarlib, "RARGetDllVersion");
|
|
if (pRAROpenArchiveEx && pRARReadHeaderEx && pRARProcessFile && pRARCloseArchive && pRARSetCallback) {
|
|
int version = -1;
|
|
israr = 1;
|
|
if (pRARGetDllVersion)
|
|
version = pRARGetDllVersion ();
|
|
write_log (_T("%s version %08X detected\n"), tmp, version);
|
|
if (version < 4) {
|
|
write_log (_T("Too old unrar.dll, must be at least version 4\n"));
|
|
israr = -1;
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
return israr < 0 ? 0 : 1;
|
|
}
|
|
|
|
static int CALLBACK RARCallbackProc (UINT msg, LPARAM UserData, LPARAM P1, LPARAM P2)
|
|
{
|
|
if (msg == UCM_PROCESSDATA) {
|
|
zfile_fwrite ((uae_u8*)P1, 1, P2, rarunpackzf);
|
|
return 0;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
struct RARContext
|
|
{
|
|
struct RAROpenArchiveDataEx OpenArchiveData;
|
|
struct RARHeaderDataEx HeaderData;
|
|
HANDLE hArcData;
|
|
};
|
|
|
|
static void archive_close_rar (void *ctx)
|
|
{
|
|
struct RARContext* rc = (struct RARContext*)ctx;
|
|
xfree (rc);
|
|
}
|
|
|
|
struct zvolume *archive_directory_rar (struct zfile *z)
|
|
{
|
|
struct zvolume *zv;
|
|
struct RARContext *rc;
|
|
struct zfile *zftmp;
|
|
int cnt;
|
|
|
|
if (!canrar ())
|
|
return archive_directory_arcacc (z, ArchiveFormatRAR);
|
|
if (z->data)
|
|
/* wtf? stupid unrar.dll only accept filename as an input.. */
|
|
return archive_directory_arcacc (z, ArchiveFormatRAR);
|
|
rc = xcalloc (struct RARContext, 1);
|
|
zv = zvolume_alloc (z, ArchiveFormatRAR, rc, NULL);
|
|
rc->OpenArchiveData.ArcNameW = z->name;
|
|
rc->OpenArchiveData.OpenMode = RAR_OM_LIST;
|
|
rc->hArcData = pRAROpenArchiveEx (&rc->OpenArchiveData);
|
|
if (rc->OpenArchiveData.OpenResult != 0) {
|
|
zfile_fclose_archive (zv);
|
|
return archive_directory_arcacc (z, ArchiveFormatRAR);
|
|
}
|
|
pRARSetCallback (rc->hArcData, RARCallbackProc, 0);
|
|
cnt = 0;
|
|
while (pRARReadHeaderEx (rc->hArcData, &rc->HeaderData) == 0) {
|
|
struct zarchive_info zai;
|
|
struct znode *zn;
|
|
memset (&zai, 0, sizeof zai);
|
|
zai.name = rc->HeaderData.FileNameW;
|
|
zai.size = rc->HeaderData.UnpSize;
|
|
zai.flags = -1;
|
|
zai.tv.tv_sec = fromdostime (rc->HeaderData.FileTime);
|
|
zn = zvolume_addfile_abs (zv, &zai);
|
|
if (zn)
|
|
zn->offset = cnt++;
|
|
pRARProcessFile (rc->hArcData, RAR_SKIP, NULL, NULL);
|
|
}
|
|
pRARCloseArchive (rc->hArcData);
|
|
zftmp = zfile_fopen_empty (z, z->name, 0);
|
|
zv->archive = zftmp;
|
|
zv->method = ArchiveFormatRAR;
|
|
return zv;
|
|
}
|
|
|
|
static struct zfile *archive_access_rar (struct znode *zn)
|
|
{
|
|
struct RARContext *rc = (struct RARContext*)zn->volume->handle;
|
|
int i;
|
|
struct zfile *zf = NULL;
|
|
|
|
if (zn->volume->method != ArchiveFormatRAR)
|
|
return archive_access_arcacc (zn);
|
|
rc->OpenArchiveData.OpenMode = RAR_OM_EXTRACT;
|
|
rc->hArcData = pRAROpenArchiveEx (&rc->OpenArchiveData);
|
|
if (rc->OpenArchiveData.OpenResult != 0)
|
|
return NULL;
|
|
pRARSetCallback (rc->hArcData, RARCallbackProc, 0);
|
|
for (i = 0; i <= zn->offset; i++) {
|
|
if (pRARReadHeaderEx (rc->hArcData, &rc->HeaderData))
|
|
return NULL;
|
|
if (i < zn->offset) {
|
|
if (pRARProcessFile (rc->hArcData, RAR_SKIP, NULL, NULL))
|
|
goto end;
|
|
}
|
|
}
|
|
zf = zfile_fopen_empty (zn->volume->archive, zn->fullname, zn->size);
|
|
if (zf) {
|
|
rarunpackzf = zf;
|
|
if (pRARProcessFile (rc->hArcData, RAR_TEST, NULL, NULL)) {
|
|
zfile_fclose (zf);
|
|
zf = NULL;
|
|
}
|
|
}
|
|
end:
|
|
pRARCloseArchive(rc->hArcData);
|
|
return zf;
|
|
}
|
|
|
|
#endif
|
|
|
|
/* ArchiveAccess */
|
|
|
|
|
|
#if defined(ARCHIVEACCESS)
|
|
|
|
struct aaFILETIME
|
|
{
|
|
uae_u32 dwLowDateTime;
|
|
uae_u32 dwHighDateTime;
|
|
};
|
|
typedef void* aaHandle;
|
|
// This struct contains file information from an archive. The caller may store
|
|
// this information for accessing this file after calls to findFirst, findNext
|
|
#define FileInArchiveInfoStringSize 1024
|
|
struct aaFileInArchiveInfo {
|
|
int ArchiveHandle; // handle for Archive/class pointer
|
|
uae_u64 CompressedFileSize;
|
|
uae_u64 UncompressedFileSize;
|
|
uae_u32 attributes;
|
|
int IsDir;
|
|
struct aaFILETIME LastWriteTime;
|
|
char path[FileInArchiveInfoStringSize];
|
|
};
|
|
|
|
typedef HRESULT (__stdcall *aaReadCallback)(int StreamID, uae_u64 offset, uae_u32 count, void* buf, uae_u32 *processedSize);
|
|
typedef HRESULT (__stdcall *aaWriteCallback)(int StreamID, uae_u64 offset, uae_u32 count, const void *buf, uae_u32 *processedSize);
|
|
typedef aaHandle (__stdcall *aapOpenArchive)(aaReadCallback function, int StreamID, uae_u64 FileSize, int ArchiveType, int *result, TCHAR *password);
|
|
typedef int (__stdcall *aapGetFileCount)(aaHandle ArchiveHandle);
|
|
typedef int (__stdcall *aapGetFileInfo)(aaHandle ArchiveHandle, int FileNum, struct aaFileInArchiveInfo *FileInfo);
|
|
typedef int (__stdcall *aapExtract)(aaHandle ArchiveHandle, int FileNum, int StreamID, aaWriteCallback WriteFunc, uae_u64 *written);
|
|
typedef int (__stdcall *aapCloseArchive)(aaHandle ArchiveHandle);
|
|
|
|
static aapOpenArchive aaOpenArchive;
|
|
static aapGetFileCount aaGetFileCount;
|
|
static aapGetFileInfo aaGetFileInfo;
|
|
static aapExtract aaExtract;
|
|
static aapCloseArchive aaCloseArchive;
|
|
|
|
#ifdef _WIN32
|
|
static HMODULE arcacc_mod;
|
|
|
|
static void arcacc_free (void)
|
|
{
|
|
if (arcacc_mod)
|
|
FreeLibrary (arcacc_mod);
|
|
arcacc_mod = NULL;
|
|
}
|
|
|
|
static int arcacc_init (struct zfile *zf)
|
|
{
|
|
if (arcacc_mod)
|
|
return 1;
|
|
arcacc_mod = WIN32_LoadLibrary (_T("archiveaccess.dll"));
|
|
if (!arcacc_mod) {
|
|
write_log (_T("failed to open archiveaccess.dll ('%s')\n"), zfile_getname (zf));
|
|
return 0;
|
|
}
|
|
aaOpenArchive = (aapOpenArchive) GetProcAddress (arcacc_mod, "aaOpenArchive");
|
|
aaGetFileCount = (aapGetFileCount) GetProcAddress (arcacc_mod, "aaGetFileCount");
|
|
aaGetFileInfo = (aapGetFileInfo) GetProcAddress (arcacc_mod, "aaGetFileInfo");
|
|
aaExtract = (aapExtract) GetProcAddress (arcacc_mod, "aaExtract");
|
|
aaCloseArchive = (aapCloseArchive) GetProcAddress (arcacc_mod, "aaCloseArchive");
|
|
if (!aaOpenArchive || !aaGetFileCount || !aaGetFileInfo || !aaExtract || !aaCloseArchive) {
|
|
write_log (_T("Missing functions in archiveaccess.dll. Old version?\n"));
|
|
arcacc_free ();
|
|
return 0;
|
|
}
|
|
return 1;
|
|
}
|
|
#endif
|
|
|
|
#define ARCACC_STACKSIZE 10
|
|
static struct zfile *arcacc_stack[ARCACC_STACKSIZE];
|
|
static int arcacc_stackptr = -1;
|
|
|
|
static int arcacc_push (struct zfile *f)
|
|
{
|
|
if (arcacc_stackptr == ARCACC_STACKSIZE - 1)
|
|
return -1;
|
|
arcacc_stackptr++;
|
|
arcacc_stack[arcacc_stackptr] = f;
|
|
return arcacc_stackptr;
|
|
}
|
|
static void arcacc_pop (void)
|
|
{
|
|
arcacc_stackptr--;
|
|
}
|
|
|
|
static HRESULT __stdcall readCallback (int StreamID, uae_u64 offset, uae_u32 count, void *buf, uae_u32 *processedSize)
|
|
{
|
|
struct zfile *f = arcacc_stack[StreamID];
|
|
int ret;
|
|
|
|
zfile_fseek (f, (long)offset, SEEK_SET);
|
|
ret = zfile_fread (buf, 1, count, f);
|
|
if (processedSize)
|
|
*processedSize = ret;
|
|
return 0;
|
|
}
|
|
static HRESULT __stdcall writeCallback (int StreamID, uae_u64 offset, uae_u32 count, const void *buf, uae_u32 *processedSize)
|
|
{
|
|
struct zfile *f = arcacc_stack[StreamID];
|
|
int ret;
|
|
|
|
ret = zfile_fwrite ((void*)buf, 1, count, f);
|
|
if (processedSize)
|
|
*processedSize = ret;
|
|
if (ret != count)
|
|
return -1;
|
|
return 0;
|
|
}
|
|
|
|
struct zvolume *archive_directory_arcacc (struct zfile *z, unsigned int id)
|
|
{
|
|
aaHandle ah;
|
|
int id_r, status;
|
|
int fc, f;
|
|
struct zvolume *zv;
|
|
int skipsize = 0;
|
|
|
|
if (!arcacc_init (z))
|
|
return NULL;
|
|
zv = zvolume_alloc (z, ArchiveFormatAA, NULL, NULL);
|
|
id_r = arcacc_push (z);
|
|
ah = aaOpenArchive (readCallback, id_r, zv->archivesize, id, &status, NULL);
|
|
if (!status) {
|
|
zv->handle = ah;
|
|
fc = aaGetFileCount (ah);
|
|
for (f = 0; f < fc; f++) {
|
|
struct aaFileInArchiveInfo fi;
|
|
TCHAR *name;
|
|
struct znode *zn;
|
|
struct zarchive_info zai;
|
|
|
|
memset (&fi, 0, sizeof (fi));
|
|
aaGetFileInfo (ah, f, &fi);
|
|
if (fi.IsDir)
|
|
continue;
|
|
|
|
name = au (fi.path);
|
|
memset (&zai, 0, sizeof zai);
|
|
zai.name = name;
|
|
zai.flags = -1;
|
|
zai.size = (unsigned int)fi.UncompressedFileSize;
|
|
zn = zvolume_addfile_abs (zv, &zai);
|
|
if (zn) {
|
|
zn->offset = f;
|
|
zn->method = id;
|
|
}
|
|
xfree(name);
|
|
|
|
if (id == ArchiveFormat7Zip) {
|
|
if (fi.CompressedFileSize)
|
|
skipsize = 0;
|
|
skipsize += (int)fi.UncompressedFileSize;
|
|
}
|
|
}
|
|
aaCloseArchive (ah);
|
|
}
|
|
arcacc_pop ();
|
|
zv->method = ArchiveFormatAA;
|
|
return zv;
|
|
}
|
|
|
|
|
|
static struct zfile *archive_access_arcacc (struct znode *zn)
|
|
{
|
|
struct zfile *zf = NULL;
|
|
struct zfile *z = zn->volume->archive;
|
|
int status, id_r, id_w;
|
|
aaHandle ah;
|
|
int ok = 0;
|
|
|
|
id_r = arcacc_push (z);
|
|
ah = aaOpenArchive (readCallback, id_r, zn->volume->archivesize, zn->method, &status, NULL);
|
|
if (!status) {
|
|
int err;
|
|
uae_u64 written = 0;
|
|
struct aaFileInArchiveInfo fi;
|
|
memset (&fi, 0, sizeof (fi));
|
|
aaGetFileInfo (ah, zn->offset, &fi);
|
|
zf = zfile_fopen_empty (z, zn->fullname, zn->size);
|
|
id_w = arcacc_push (zf);
|
|
err = aaExtract(ah, zn->offset, id_w, writeCallback, &written);
|
|
if (zf->seek == fi.UncompressedFileSize)
|
|
ok = 1;
|
|
arcacc_pop();
|
|
}
|
|
aaCloseArchive(ah);
|
|
arcacc_pop();
|
|
if (ok)
|
|
return zf;
|
|
zfile_fclose(zf);
|
|
return NULL;
|
|
}
|
|
#endif
|
|
|
|
/* plain single file */
|
|
|
|
static struct znode *addfile (struct zvolume *zv, struct zfile *zf, const TCHAR *path, uae_u8 *data, int size)
|
|
{
|
|
struct zarchive_info zai;
|
|
struct znode *zn;
|
|
struct zfile *z;
|
|
|
|
z = zfile_fopen_empty (zf, path, size);
|
|
if (!z)
|
|
return NULL;
|
|
zfile_fwrite (data, size, 1, z);
|
|
memset (&zai, 0, sizeof zai);
|
|
zai.name = my_strdup (path);
|
|
zai.flags = -1;
|
|
zai.size = size;
|
|
zn = zvolume_addfile_abs (zv, &zai);
|
|
if (zn)
|
|
zn->f = z;
|
|
else
|
|
zfile_fclose (z);
|
|
xfree (zai.name);
|
|
return zn;
|
|
}
|
|
|
|
static uae_u8 exeheader[]={0x00,0x00,0x03,0xf3,0x00,0x00,0x00,0x00};
|
|
struct zvolume *archive_directory_plain (struct zfile *z)
|
|
{
|
|
struct zfile *zf, *zf2;
|
|
struct zvolume *zv;
|
|
struct znode *zn;
|
|
struct zarchive_info zai;
|
|
uae_u8 id[8];
|
|
int rc, index;
|
|
|
|
memset (&zai, 0, sizeof zai);
|
|
zv = zvolume_alloc (z, ArchiveFormatPLAIN, NULL, NULL);
|
|
memset(id, 0, sizeof id);
|
|
zai.name = zfile_getfilename (z);
|
|
zai.flags = -1;
|
|
zfile_fseek(z, 0, SEEK_END);
|
|
zai.size = zfile_ftell (z);
|
|
zfile_fseek(z, 0, SEEK_SET);
|
|
zfile_fread(id, sizeof id, 1, z);
|
|
zfile_fseek(z, 0, SEEK_SET);
|
|
zn = zvolume_addfile_abs (zv, &zai);
|
|
if (!memcmp (id, exeheader, sizeof id)) {
|
|
char *an = ua (zai.name);
|
|
char *data = xmalloc (char, 1 + strlen (an) + 1 + 1 + 1);
|
|
sprintf (data, "\"%s\"\n", an);
|
|
zn = addfile (zv, z, _T("s/startup-sequence"), (uae_u8*)data, strlen (data));
|
|
xfree (data);
|
|
xfree (an);
|
|
}
|
|
index = 0;
|
|
for (;;) {
|
|
zf = zfile_dup (z);
|
|
if (!zf)
|
|
break;
|
|
zf2 = zuncompress (NULL, zf, 0, ZFD_ALL & ~ZFD_ADF, &rc, index);
|
|
if (zf2) {
|
|
zf = NULL;
|
|
zai.name = zfile_getfilename (zf2);
|
|
zai.flags = -1;
|
|
zfile_fseek (zf2, 0, SEEK_END);
|
|
zai.size = zfile_ftell (zf2);
|
|
zfile_fseek (zf2, 0, SEEK_SET);
|
|
zn = zvolume_addfile_abs (zv, &zai);
|
|
if (zn)
|
|
zn->f = zf2;
|
|
// if (zn)
|
|
// zn->offset = index + 1;
|
|
// zfile_fclose (zf2);
|
|
} else {
|
|
if (rc == 0) {
|
|
zfile_fclose (zf);
|
|
break;
|
|
}
|
|
}
|
|
index++;
|
|
zfile_fclose (zf);
|
|
}
|
|
return zv;
|
|
}
|
|
static struct zfile *archive_access_plain (struct znode *zn)
|
|
{
|
|
struct zfile *z;
|
|
|
|
if (zn->offset) {
|
|
struct zfile *zf;
|
|
z = zfile_fopen_empty (zn->volume->archive, zn->fullname, zn->size);
|
|
zf = zfile_fopen (zfile_getname (zn->volume->archive), _T("rb"), zn->volume->archive->zfdmask & ~ZFD_ADF, zn->offset - 1);
|
|
if (zf) {
|
|
zfile_fread (z->data, zn->size, 1, zf);
|
|
zfile_fclose (zf);
|
|
}
|
|
} else {
|
|
z = zfile_fopen_empty (zn->volume->archive, zn->fullname, zn->size);
|
|
if (z) {
|
|
zfile_fseek (zn->volume->archive, 0, SEEK_SET);
|
|
zfile_fread (z->data, zn->size, 1, zn->volume->archive);
|
|
}
|
|
}
|
|
return z;
|
|
}
|
|
|
|
struct adfhandle {
|
|
int size;
|
|
int highblock;
|
|
int blocksize;
|
|
int rootblock;
|
|
struct zfile *z;
|
|
uae_u8 block[65536];
|
|
uae_u32 dostype;
|
|
};
|
|
|
|
|
|
static int dos_checksum (uae_u8 *p, int blocksize)
|
|
{
|
|
uae_u32 cs = 0;
|
|
int i;
|
|
for (i = 0; i < blocksize; i += 4)
|
|
cs += (p[i] << 24) | (p[i + 1] << 16) | (p[i + 2] << 8) | (p[i + 3] << 0);
|
|
return cs;
|
|
}
|
|
static int sfs_checksum (uae_u8 *p, int blocksize, int sfs2)
|
|
{
|
|
uae_u32 cs = sfs2 ? 2 : 1;
|
|
int i;
|
|
for (i = 0; i < blocksize; i += 4)
|
|
cs += (p[i] << 24) | (p[i + 1] << 16) | (p[i + 2] << 8) | (p[i + 3] << 0);
|
|
return cs;
|
|
}
|
|
|
|
static TCHAR *getBSTR (uae_u8 *bstr)
|
|
{
|
|
int n = *bstr++;
|
|
uae_char buf[257];
|
|
int i;
|
|
|
|
for (i = 0; i < n; i++)
|
|
buf[i] = *bstr++;
|
|
buf[i] = 0;
|
|
return au (buf);
|
|
}
|
|
|
|
static uae_u32 gl (struct adfhandle *adf, int off)
|
|
{
|
|
uae_u8 *p = adf->block + off;
|
|
return (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | (p[3] << 0);
|
|
}
|
|
static uae_u32 glx (uae_u8 *p)
|
|
{
|
|
return (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | (p[3] << 0);
|
|
}
|
|
static uae_u32 gwx (uae_u8 *p)
|
|
{
|
|
return (p[0] << 8) | (p[1] << 0);
|
|
}
|
|
|
|
static const int secs_per_day = 24 * 60 * 60;
|
|
static const int diff = (8 * 365 + 2) * (24 * 60 * 60);
|
|
static const int diff2 = (-8 * 365 - 2) * (24 * 60 * 60);
|
|
|
|
static int adf_read_block (struct adfhandle *adf, int block)
|
|
{
|
|
memset (adf->block, 0, adf->blocksize);
|
|
if (block >= adf->highblock || block < 0)
|
|
return 0;
|
|
zfile_fseek (adf->z, block * adf->blocksize, SEEK_SET);
|
|
zfile_fread (adf->block, adf->blocksize, 1, adf->z);
|
|
return 1;
|
|
}
|
|
|
|
static void recurseadf (struct znode *zn, int root, TCHAR *name)
|
|
{
|
|
int i;
|
|
struct zvolume *zv = zn->volume;
|
|
struct adfhandle *adf = (struct adfhandle*)zv->handle;
|
|
TCHAR name2[MAX_DPATH];
|
|
int bs = adf->blocksize;
|
|
|
|
for (i = 0; i < bs / 4 - 56; i++) {
|
|
int block;
|
|
if (!adf_read_block (adf, root))
|
|
return;
|
|
block = gl (adf, (i + 6) * 4);
|
|
while (block > 0 && block < adf->size / bs) {
|
|
struct zarchive_info zai;
|
|
TCHAR *fname;
|
|
uae_u32 size, secondary;
|
|
|
|
if (!adf_read_block (adf, block))
|
|
return;
|
|
if (gl (adf, 0) != 2)
|
|
break;
|
|
if (gl (adf, 1 * 4) != block)
|
|
break;
|
|
secondary = gl (adf, bs - 1 * 4);
|
|
if (secondary != -3 && secondary != 2)
|
|
break;
|
|
memset (&zai, 0, sizeof zai);
|
|
fname = getBSTR (adf->block + bs - 20 * 4);
|
|
size = gl (adf, bs - 47 * 4);
|
|
name2[0] = 0;
|
|
if (name[0]) {
|
|
TCHAR sep[] = { FSDB_DIR_SEPARATOR, 0 };
|
|
_tcscpy (name2, name);
|
|
_tcscat (name2, sep);
|
|
}
|
|
_tcscat (name2, fname);
|
|
zai.name = name2;
|
|
if (size < 0 || size > 0x7fffffff)
|
|
size = 0;
|
|
zai.size = size;
|
|
zai.flags = gl (adf, bs - 48 * 4);
|
|
amiga_to_timeval (&zai.tv, gl (adf, bs - 23 * 4), gl (adf, bs - 22 * 4),gl (adf, bs - 21 * 4), 50);
|
|
if (secondary == -3) {
|
|
struct znode *znnew = zvolume_addfile_abs (zv, &zai);
|
|
if (znnew)
|
|
znnew->offset = block;
|
|
} else {
|
|
struct znode *znnew = zvolume_adddir_abs (zv, &zai);
|
|
if (znnew) {
|
|
znnew->offset = block;
|
|
recurseadf (znnew, block, name2);
|
|
}
|
|
if (!adf_read_block (adf, block))
|
|
return;
|
|
}
|
|
xfree (fname);
|
|
block = gl (adf, bs - 4 * 4);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void recursesfs (struct znode *zn, int root, TCHAR *name, int sfs2)
|
|
{
|
|
struct zvolume *zv = zn->volume;
|
|
struct adfhandle *adf = (struct adfhandle*)zv->handle;
|
|
TCHAR name2[MAX_DPATH];
|
|
int block;
|
|
uae_u8 *p, *s;
|
|
struct zarchive_info zai;
|
|
|
|
block = root;
|
|
while (block) {
|
|
if (!adf_read_block (adf, block))
|
|
return;
|
|
p = adf->block + 12 + 3 * 4;
|
|
while (glx (p + 4) && p < adf->block + adf->blocksize - 27) {
|
|
TCHAR *fname;
|
|
int i;
|
|
int align;
|
|
|
|
memset (&zai, 0, sizeof zai);
|
|
zai.flags = glx (p + 8) ^ 0x0f;
|
|
s = p + (sfs2 ? 27 : 25);
|
|
fname = au ((char*)s);
|
|
i = 0;
|
|
while (*s) {
|
|
s++;
|
|
i++;
|
|
}
|
|
s++;
|
|
i++;
|
|
if (*s)
|
|
zai.comment = au ((char*)s);
|
|
while (*s) {
|
|
s++;
|
|
i++;
|
|
}
|
|
s++;
|
|
i++;
|
|
i += sfs2 ? 27 : 25;
|
|
align = i & 1;
|
|
|
|
name2[0] = 0;
|
|
if (name[0]) {
|
|
TCHAR sep[] = { FSDB_DIR_SEPARATOR, 0 };
|
|
_tcscpy (name2, name);
|
|
_tcscat (name2, sep);
|
|
}
|
|
_tcscat (name2, fname);
|
|
zai.name = name2;
|
|
if (sfs2)
|
|
zai.tv.tv_sec = glx (p + 22) - diff2;
|
|
else
|
|
zai.tv.tv_sec = glx (p + 20) - diff;
|
|
if (p[sfs2 ? 26 : 24] & 0x80) { // dir
|
|
struct znode *znnew = zvolume_adddir_abs (zv, &zai);
|
|
int newblock = glx (p + 16);
|
|
if (newblock) {
|
|
znnew->offset = block;
|
|
recursesfs (znnew, newblock, name2, sfs2);
|
|
}
|
|
if (!adf_read_block (adf, block))
|
|
return;
|
|
} else {
|
|
struct znode *znnew;
|
|
if (sfs2) {
|
|
uae_u64 b1 = p[16];
|
|
uae_u64 b2 = p[17];
|
|
zai.size = (b1 << 40) | (b2 << 32) | glx (p + 18) ;
|
|
} else {
|
|
zai.size = glx (p + 16);
|
|
}
|
|
znnew = zvolume_addfile_abs (zv, &zai);
|
|
if (znnew) {
|
|
znnew->offset = block;
|
|
znnew->offset2 = p - adf->block;
|
|
}
|
|
}
|
|
xfree (zai.comment);
|
|
xfree (fname);
|
|
p += i + align;
|
|
}
|
|
block = gl (adf, 12 + 4);
|
|
}
|
|
|
|
}
|
|
|
|
struct zvolume *archive_directory_adf (struct znode *parent, struct zfile *z)
|
|
{
|
|
struct zvolume *zv;
|
|
struct adfhandle *adf;
|
|
TCHAR name[MAX_DPATH];
|
|
int gotroot = 0;
|
|
|
|
adf = xcalloc (struct adfhandle, 1);
|
|
zfile_fseek (z, 0, SEEK_END);
|
|
adf->size = zfile_ftell (z);
|
|
zfile_fseek (z, 0, SEEK_SET);
|
|
|
|
adf->blocksize = 512;
|
|
if (parent && parent->offset2) {
|
|
if (parent->offset2 == 1024 || parent->offset2 == 2048 || parent->offset2 == 4096 || parent->offset2 == 8192 ||
|
|
parent->offset2 == 16384 || parent->offset2 == 32768 || parent->offset2 == 65536) {
|
|
adf->blocksize = parent->offset2;
|
|
gotroot = 1;
|
|
}
|
|
}
|
|
|
|
adf->highblock = adf->size / adf->blocksize;
|
|
adf->z = z;
|
|
|
|
if (!adf_read_block (adf, 0))
|
|
goto fail;
|
|
adf->dostype = gl (adf, 0);
|
|
|
|
if ((adf->dostype & 0xffffff00) == MCC('D', 'O', 'S', '\0')) {
|
|
int bs = adf->blocksize;
|
|
int res;
|
|
|
|
adf->rootblock = ((adf->size / bs) - 1 + 2) / 2;
|
|
if (!gotroot) {
|
|
for (res = 2; res >= 1; res--) {
|
|
for (bs = 512; bs < 65536; bs <<= 1) {
|
|
adf->blocksize = bs;
|
|
adf->rootblock = ((adf->size / bs) - 1 + res) / 2;
|
|
if (!adf_read_block (adf, adf->rootblock))
|
|
continue;
|
|
if (gl (adf, 0) != 2 || gl (adf, bs - 1 * 4) != 1)
|
|
continue;
|
|
if (dos_checksum (adf->block, bs) != 0)
|
|
continue;
|
|
gotroot = 1;
|
|
break;
|
|
}
|
|
if (gotroot)
|
|
break;
|
|
}
|
|
}
|
|
if (!gotroot) {
|
|
bs = adf->blocksize = 512;
|
|
if (adf->size < 2000000 && adf->rootblock != 880) {
|
|
adf->rootblock = 880;
|
|
if (!adf_read_block (adf, adf->rootblock))
|
|
goto fail;
|
|
if (gl (adf, 0) != 2 || gl (adf, bs - 1 * 4) != 1)
|
|
goto fail;
|
|
if (dos_checksum (adf->block, bs) != 0)
|
|
goto fail;
|
|
}
|
|
}
|
|
|
|
if (!adf_read_block (adf, adf->rootblock))
|
|
goto fail;
|
|
if (gl (adf, 0) != 2 || gl (adf, bs - 1 * 4) != 1)
|
|
goto fail;
|
|
if (dos_checksum (adf->block, adf->blocksize) != 0)
|
|
goto fail;
|
|
adf->blocksize = bs;
|
|
adf->highblock = adf->size / adf->blocksize;
|
|
zv = zvolume_alloc (z, ArchiveFormatADF, NULL, NULL);
|
|
zv->method = ArchiveFormatADF;
|
|
zv->handle = adf;
|
|
zv->volumename = getBSTR (adf->block + adf->blocksize - 20 * 4);
|
|
|
|
name[0] = 0;
|
|
recurseadf (&zv->root, adf->rootblock, name);
|
|
|
|
} else if ((adf->dostype & 0xffffff00) == MCC('S', 'F', 'S', '\0')) {
|
|
|
|
uae_u16 version, sfs2;
|
|
|
|
for (;;) {
|
|
for (;;) {
|
|
version = gl (adf, 12) >> 16;
|
|
sfs2 = version > 3;
|
|
if (version > 4)
|
|
break;
|
|
adf->rootblock = gl (adf, 104);
|
|
if (!adf_read_block (adf, adf->rootblock))
|
|
break;
|
|
if (gl (adf, 0) != MCC('O', 'B', 'J', 'C'))
|
|
break;
|
|
if (sfs_checksum (adf->block, adf->blocksize, sfs2))
|
|
break;
|
|
adf->rootblock = gl (adf, 40);
|
|
if (!adf_read_block (adf, adf->rootblock))
|
|
break;
|
|
if (gl (adf, 0) != MCC('O', 'B', 'J', 'C'))
|
|
break;
|
|
if (sfs_checksum (adf->block, adf->blocksize, sfs2))
|
|
break;
|
|
gotroot = 1;
|
|
break;
|
|
}
|
|
if (gotroot)
|
|
break;
|
|
adf->blocksize <<= 1;
|
|
if (adf->blocksize == 65536)
|
|
break;
|
|
}
|
|
if (!gotroot)
|
|
goto fail;
|
|
|
|
zv = zvolume_alloc (z, ArchiveFormatADF, NULL, NULL);
|
|
zv->method = ArchiveFormatADF;
|
|
zv->handle = adf;
|
|
|
|
name[0] = 0;
|
|
recursesfs (&zv->root, adf->rootblock, name, version > 3);
|
|
|
|
} else {
|
|
goto fail;
|
|
}
|
|
|
|
return zv;
|
|
fail:
|
|
xfree (adf);
|
|
return NULL;
|
|
}
|
|
|
|
struct sfsblock
|
|
{
|
|
int block;
|
|
int length;
|
|
};
|
|
|
|
static int sfsfindblock (struct adfhandle *adf, int btree, int theblock, struct sfsblock **sfsb, int *sfsblockcnt, int *sfsmaxblockcnt, int sfs2)
|
|
{
|
|
int nodecount, isleaf, nodesize;
|
|
int i;
|
|
uae_u8 *p;
|
|
|
|
if (!btree)
|
|
return 0;
|
|
if (!adf_read_block (adf, btree))
|
|
return 0;
|
|
if (memcmp (adf->block, "BNDC", 4))
|
|
return 0;
|
|
nodecount = gwx (adf->block + 12);
|
|
isleaf = adf->block[14];
|
|
nodesize = adf->block[15];
|
|
p = adf->block + 16;
|
|
for (i = 0; i < nodecount; i++) {
|
|
if (isleaf) {
|
|
uae_u32 key = glx (p);
|
|
uae_u32 next = glx (p + 4);
|
|
uae_u32 prev = glx (p + 8);
|
|
uae_u32 blocks;
|
|
if (sfs2)
|
|
blocks = glx (p + 12);
|
|
else
|
|
blocks = gwx (p + 12);
|
|
if (key == theblock) {
|
|
struct sfsblock *sb;
|
|
if (*sfsblockcnt >= *sfsmaxblockcnt) {
|
|
*sfsmaxblockcnt += 100;
|
|
*sfsb = xrealloc (struct sfsblock, *sfsb, *sfsmaxblockcnt);
|
|
}
|
|
sb = *sfsb + (*sfsblockcnt);
|
|
sb->block = key;
|
|
sb->length = blocks;
|
|
(*sfsblockcnt)++;
|
|
return next;
|
|
}
|
|
} else {
|
|
uae_u32 key = glx (p);
|
|
uae_u32 data = glx (p + 4);
|
|
int newblock = sfsfindblock (adf, data, theblock, sfsb, sfsblockcnt, sfsmaxblockcnt, sfs2);
|
|
if (newblock)
|
|
return newblock;
|
|
if (!adf_read_block (adf, btree))
|
|
return 0;
|
|
if (memcmp (adf->block, "BNDC", 4))
|
|
return 0;
|
|
}
|
|
p += nodesize;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
static struct zfile *archive_access_adf (struct znode *zn)
|
|
{
|
|
struct zfile *z = NULL;
|
|
int root, ffs;
|
|
struct adfhandle *adf = (struct adfhandle*)zn->volume->handle;
|
|
uae_s64 size;
|
|
int i, bs;
|
|
uae_u8 *dst;
|
|
|
|
size = zn->size;
|
|
bs = adf->blocksize;
|
|
z = zfile_fopen_empty (zn->volume->archive, zn->fullname, size);
|
|
if (!z)
|
|
return NULL;
|
|
|
|
if ((adf->dostype & 0xffffff00) == MCC('D', 'O', 'S', '\0')) {
|
|
|
|
ffs = adf->dostype & 1;
|
|
root = zn->offset;
|
|
dst = z->data;
|
|
for (;;) {
|
|
adf_read_block (adf, root);
|
|
for (i = bs / 4 - 51; i >= 6; i--) {
|
|
uae_s64 bsize = ffs ? bs : bs - 24;
|
|
int block = gl (adf, i * 4);
|
|
if (size < bsize)
|
|
bsize = size;
|
|
if (ffs)
|
|
zfile_fseek (adf->z, block * adf->blocksize, SEEK_SET);
|
|
else
|
|
zfile_fseek (adf->z, block * adf->blocksize + 24, SEEK_SET);
|
|
zfile_fread (dst, bsize, 1, adf->z);
|
|
size -= bsize;
|
|
dst += bsize;
|
|
if (size <= 0)
|
|
break;
|
|
}
|
|
if (size <= 0)
|
|
break;
|
|
root = gl (adf, bs - 2 * 4);
|
|
}
|
|
} else if ((adf->dostype & 0xffffff00) == MCC('S', 'F', 'S', '\0')) {
|
|
|
|
struct sfsblock *sfsblocks;
|
|
int sfsblockcnt, sfsmaxblockcnt, i;
|
|
uae_s64 bsize;
|
|
int block = zn->offset;
|
|
int dblock;
|
|
int btree, version, sfs2;
|
|
uae_u8 *p;
|
|
|
|
if (!adf_read_block (adf, 0))
|
|
goto end;
|
|
btree = glx (adf->block + 108);
|
|
version = gwx (adf->block + 12);
|
|
sfs2 = version > 3;
|
|
|
|
if (!adf_read_block (adf, block))
|
|
goto end;
|
|
p = adf->block + zn->offset2;
|
|
dblock = glx (p + 12);
|
|
|
|
sfsblockcnt = 0;
|
|
sfsmaxblockcnt = 0;
|
|
sfsblocks = NULL;
|
|
if (size > 0) {
|
|
int nextblock = dblock;
|
|
while (nextblock) {
|
|
nextblock = sfsfindblock (adf, btree, nextblock, &sfsblocks, &sfsblockcnt, &sfsmaxblockcnt, sfs2);
|
|
}
|
|
}
|
|
|
|
bsize = 0;
|
|
for (i = 0; i < sfsblockcnt; i++)
|
|
bsize += sfsblocks[i].length * adf->blocksize;
|
|
if (bsize < size)
|
|
write_log (_T("SFS extracting error, %s size mismatch %d<%d\n"), z->name, bsize, size);
|
|
|
|
dst = z->data;
|
|
block = zn->offset;
|
|
for (i = 0; i < sfsblockcnt; i++) {
|
|
block = sfsblocks[i].block;
|
|
bsize = sfsblocks[i].length * adf->blocksize;
|
|
zfile_fseek (adf->z, block * adf->blocksize, SEEK_SET);
|
|
if (bsize > size)
|
|
bsize = size;
|
|
zfile_fread (dst, bsize, 1, adf->z);
|
|
dst += bsize;
|
|
size -= bsize;
|
|
}
|
|
|
|
xfree (sfsblocks);
|
|
}
|
|
return z;
|
|
end:
|
|
zfile_fclose (z);
|
|
return NULL;
|
|
}
|
|
|
|
static void archive_close_adf (void *v)
|
|
{
|
|
struct adfhandle *adf = (struct adfhandle*)v;
|
|
xfree (adf);
|
|
}
|
|
|
|
static int rl (uae_u8 *p)
|
|
{
|
|
return (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | (p[3]);
|
|
}
|
|
static TCHAR *tochar (uae_u8 *s, int len)
|
|
{
|
|
int i, j;
|
|
uae_char tmp[256];
|
|
j = 0;
|
|
for (i = 0; i < len; i++) {
|
|
uae_char c = *s++;
|
|
if (c >= 0 && c <= 9) {
|
|
tmp[j++] = FSDB_DIR_SEPARATOR;
|
|
tmp[j++] = '0' + c;
|
|
} else if (c < ' ' || c > 'z') {
|
|
tmp[j++] = '.';
|
|
} else {
|
|
tmp[j++] = c;
|
|
}
|
|
tmp[j] = 0;
|
|
}
|
|
return au (tmp);
|
|
}
|
|
|
|
struct zvolume *archive_directory_rdb (struct zfile *z)
|
|
{
|
|
uae_u8 buf[512] = { 0 };
|
|
int partnum, bs;
|
|
TCHAR *devname;
|
|
struct zvolume *zv;
|
|
struct zarchive_info zai;
|
|
uae_u8 *p;
|
|
struct znode *zn;
|
|
|
|
zv = zvolume_alloc (z, ArchiveFormatRDB, NULL, NULL);
|
|
|
|
zfile_fseek (z, 0, SEEK_SET);
|
|
zfile_fread (buf, 1, 512, z);
|
|
|
|
partnum = 0;
|
|
for (;;) {
|
|
int partblock;
|
|
TCHAR tmp[MAX_DPATH];
|
|
int surf, spt, spb, lowcyl, highcyl, reserved;
|
|
int size, block, blocksize, rootblock;
|
|
TCHAR comment[81], *dos;
|
|
|
|
if (partnum == 0)
|
|
partblock = rl (buf + 28);
|
|
else
|
|
partblock = rl (buf + 4 * 4);
|
|
partnum++;
|
|
if (partblock <= 0)
|
|
break;
|
|
zfile_fseek (z, partblock * 512, SEEK_SET);
|
|
zfile_fread (buf, 1, 512, z);
|
|
if (memcmp (buf, "PART", 4))
|
|
break;
|
|
|
|
p = buf + 128 - 16;
|
|
surf = rl (p + 28);
|
|
spb = rl (p + 32);
|
|
spt = rl (p + 36);
|
|
reserved = rl (p + 40);
|
|
lowcyl = rl (p + 52);
|
|
highcyl = rl (p + 56);
|
|
blocksize = rl (p + 20) * 4 * spb;
|
|
block = lowcyl * surf * spt;
|
|
|
|
size = (highcyl - lowcyl + 1) * surf * spt;
|
|
size *= blocksize;
|
|
|
|
dos = tochar (buf + 192, 4);
|
|
|
|
if (!memcmp (dos, _T("DOS"), 3))
|
|
rootblock = ((size / blocksize) - 1 + 2) / 2;
|
|
else
|
|
rootblock = 0;
|
|
|
|
devname = getBSTR (buf + 36);
|
|
_stprintf (tmp, _T("%s.hdf"), devname);
|
|
memset (&zai, 0, sizeof zai);
|
|
_stprintf (comment, _T("FS=%s LO=%d HI=%d HEADS=%d SPT=%d RES=%d BLOCK=%d ROOT=%d"),
|
|
dos, lowcyl, highcyl, surf, spt, reserved, blocksize, rootblock);
|
|
zai.comment = comment;
|
|
xfree (dos);
|
|
zai.name = tmp;
|
|
zai.size = size;
|
|
zai.flags = -1;
|
|
zn = zvolume_addfile_abs (zv, &zai);
|
|
if (zn) {
|
|
zn->offset = partblock;
|
|
zn->offset2 = blocksize; // abuse of offset2..
|
|
}
|
|
}
|
|
|
|
zfile_fseek (z, 0, SEEK_SET);
|
|
p = buf;
|
|
zfile_fread (buf, 1, 512, z);
|
|
zai.name = my_strdup(_T("rdb_dump.dat"));
|
|
bs = rl (p + 16);
|
|
zai.size = rl (p + 140) * bs;
|
|
zai.comment = NULL;
|
|
zn = zvolume_addfile_abs (zv, &zai);
|
|
zn->offset = 0;
|
|
|
|
zv->method = ArchiveFormatRDB;
|
|
return zv;
|
|
}
|
|
|
|
static struct zfile *archive_access_rdb (struct znode *zn)
|
|
{
|
|
struct zfile *z = zn->volume->archive;
|
|
struct zfile *zf;
|
|
uae_u8 buf[512] = { 0 };
|
|
int surf, spb, spt, lowcyl, highcyl;
|
|
int block, blocksize;
|
|
uae_s64 size;
|
|
uae_u8 *p;
|
|
|
|
if (zn->offset) {
|
|
zfile_fseek (z, zn->offset * 512, SEEK_SET);
|
|
zfile_fread (buf, 1, 512, z);
|
|
|
|
p = buf + 128 - 16;
|
|
surf = rl (p + 28);
|
|
spb = rl (p + 32);
|
|
spt = rl (p + 36);
|
|
lowcyl = rl (p + 52);
|
|
highcyl = rl (p + 56);
|
|
blocksize = rl (p + 20) * 4;
|
|
block = lowcyl * surf * spt;
|
|
|
|
size = (highcyl - lowcyl + 1) * surf * spt;
|
|
size *= blocksize;
|
|
} else {
|
|
zfile_fseek (z, 0, SEEK_SET);
|
|
zfile_fread (buf, 1, 512, z);
|
|
p = buf;
|
|
blocksize = rl (p + 16);
|
|
block = 0;
|
|
size = zn->size;
|
|
}
|
|
|
|
zf = zfile_fopen_parent (z, zn->fullname, block * blocksize, size);
|
|
return zf;
|
|
}
|
|
|
|
int isfat (uae_u8 *p)
|
|
{
|
|
int i, b;
|
|
|
|
if ((p[0x15] & 0xf0) != 0xf0)
|
|
return 0;
|
|
if (p[0x0b] != 0x00 || p[0x0c] != 0x02)
|
|
return 0;
|
|
b = 0;
|
|
for (i = 0; i < 8; i++) {
|
|
if (p[0x0d] & (1 << i))
|
|
b++;
|
|
}
|
|
if (b != 1)
|
|
return 0;
|
|
if (p[0x0f] != 0)
|
|
return 0;
|
|
if (p[0x0e] > 8 || p[0x0e] == 0)
|
|
return 0;
|
|
if (p[0x10] == 0 || p[0x10] > 8)
|
|
return 0;
|
|
b = (p[0x12] << 8) | p[0x11];
|
|
if (b > 8192 || b <= 0)
|
|
return 0;
|
|
b = p[0x16] | (p[0x17] << 8);
|
|
if (b == 0 || b > 8192)
|
|
return 0;
|
|
return 1;
|
|
}
|
|
|
|
/*
|
|
* The epoch of FAT timestamp is 1980.
|
|
* : bits : value
|
|
* date: 0 - 4: day (1 - 31)
|
|
* date: 5 - 8: month (1 - 12)
|
|
* date: 9 - 15: year (0 - 127) from 1980
|
|
* time: 0 - 4: sec (0 - 29) 2sec counts
|
|
* time: 5 - 10: min (0 - 59)
|
|
* time: 11 - 15: hour (0 - 23)
|
|
*/
|
|
#define SECS_PER_MIN 60
|
|
#define SECS_PER_HOUR (60 * 60)
|
|
#define SECS_PER_DAY (SECS_PER_HOUR * 24)
|
|
#define UNIX_SECS_1980 315532800L
|
|
#if BITS_PER_LONG == 64
|
|
#define UNIX_SECS_2108 4354819200L
|
|
#endif
|
|
/* days between 1.1.70 and 1.1.80 (2 leap days) */
|
|
#define DAYS_DELTA (365 * 10 + 2)
|
|
/* 120 (2100 - 1980) isn't leap year */
|
|
#define YEAR_2100 120
|
|
#define IS_LEAP_YEAR(y) (!((y) & 3) && (y) != YEAR_2100)
|
|
|
|
/* Linear day numbers of the respective 1sts in non-leap years. */
|
|
static time_t days_in_year[] = {
|
|
/* Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec */
|
|
0, 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 0, 0, 0,
|
|
};
|
|
|
|
/* Convert a FAT time/date pair to a UNIX date (seconds since 1 1 70). */
|
|
static time_t fat_time_fat2unix (uae_u16 time, uae_u16 date, int fat12)
|
|
{
|
|
time_t second, day, leap_day, month, year;
|
|
|
|
if (0 && fat12) {
|
|
year = date & 0x7f;
|
|
month = (date >> 7) & 0x0f;
|
|
day = (date >> 11);
|
|
} else {
|
|
year = date >> 9;
|
|
month = max(1, (date >> 5) & 0xf);
|
|
day = max(1, date & 0x1f) - 1;
|
|
}
|
|
|
|
leap_day = (year + 3) / 4;
|
|
if (year > YEAR_2100) /* 2100 isn't leap year */
|
|
leap_day--;
|
|
if (IS_LEAP_YEAR(year) && month > 2)
|
|
leap_day++;
|
|
|
|
second = (time & 0x1f) << 1;
|
|
second += ((time >> 5) & 0x3f) * SECS_PER_MIN;
|
|
second += (time >> 11) * SECS_PER_HOUR;
|
|
second += (year * 365 + leap_day
|
|
+ days_in_year[month] + day
|
|
+ DAYS_DELTA) * SECS_PER_DAY;
|
|
return second;
|
|
}
|
|
|
|
static int getcluster (struct zfile *z, int cluster, int fatstart, int fatbits)
|
|
{
|
|
uae_u32 fat = 0;
|
|
uae_u8 p[4];
|
|
int offset = cluster * fatbits;
|
|
zfile_fseek (z, fatstart * 512 + offset / 8, SEEK_SET);
|
|
if (fatbits == 12) {
|
|
zfile_fread (p, 2, 1, z);
|
|
if ((offset & 4))
|
|
fat = ((p[0] & 0xf0) >> 4) | (p[1] << 4);
|
|
else
|
|
fat = (p[0]) | ((p[1] & 0x0f) << 8);
|
|
if (fat >= 0xff0)
|
|
return -1;
|
|
} else if (fatbits == 16) {
|
|
zfile_fread (p, 2, 1, z);
|
|
fat = p[0] | (p[1] << 8);
|
|
if (fat >= 0xfff0)
|
|
return -1;
|
|
} else if (fatbits == 32) {
|
|
zfile_fread (p, 4, 1, z);
|
|
fat = p[0] | (p[1] << 8) | (p[2] << 16) | (p[3] << 24);
|
|
fat &= ~0x0fffffff;
|
|
if (fat >= 0x0ffffff0)
|
|
return -1;
|
|
}
|
|
return fat;
|
|
}
|
|
|
|
static void fatdirectory (struct zfile *z, struct zvolume *zv, const TCHAR *name, int startblock, int entries, int sectorspercluster, int fatstart, int dataregion, int fatbits)
|
|
{
|
|
struct zarchive_info zai;
|
|
struct znode *znnew;
|
|
int i, j;
|
|
|
|
for (i = 0; i < entries; i++) {
|
|
TCHAR name2[MAX_DPATH], *fname;
|
|
uae_s64 size;
|
|
uae_u8 fatname[16];
|
|
uae_u8 buf[32];
|
|
int attr, cnt, ext;
|
|
int startcluster;
|
|
|
|
memset (buf, 0, sizeof buf);
|
|
memset (&zai, 0, sizeof zai);
|
|
zfile_fseek (z, startblock * 512 + i * 32, SEEK_SET);
|
|
zfile_fread (buf, 32, 1, z);
|
|
if (buf[0] == 0)
|
|
break;
|
|
if (buf[0] == 0xe5)
|
|
continue;
|
|
if (buf[0] == 0x05)
|
|
buf[0] = 0xe5;
|
|
size = buf[0x1c] | (buf[0x1d] << 8) | (buf[0x1e] << 16) | (buf[0x1f] << 24);
|
|
attr = buf[0x0b];
|
|
startcluster = buf[0x1a] | (buf[0x1b] << 8);
|
|
if ((attr & (0x4 | 0x2)) == 0x06) // system+hidden
|
|
continue;
|
|
if (attr & 8) // disk name
|
|
continue;
|
|
if (attr & 1) // read-only
|
|
zai.flags |= 1 << 3;
|
|
if (!(attr & 32)) // archive
|
|
zai.flags |= 1 << 4;
|
|
|
|
cnt = 0;
|
|
ext = 0;
|
|
for (j = 0; j < 8 && buf[j] != 0x20 && buf[j] != 0; j++)
|
|
fatname[cnt++] = buf[j];
|
|
for (j = 0; j < 3 && buf[8 + j] != 0x20 && buf[8 + j] != 0; j++) {
|
|
if (ext == 0)
|
|
fatname[cnt++] = '.';
|
|
ext = 1;
|
|
fatname[cnt++] = buf[8 + j];
|
|
}
|
|
fatname[cnt] = 0;
|
|
|
|
fname = au ((char*)fatname);
|
|
name2[0] = 0;
|
|
if (name[0]) {
|
|
TCHAR sep[] = { FSDB_DIR_SEPARATOR, 0 };
|
|
_tcscpy (name2, name);
|
|
_tcscat (name2, sep);
|
|
}
|
|
_tcscat (name2, fname);
|
|
|
|
zai.name = name2;
|
|
zai.tv.tv_sec = fat_time_fat2unix (buf[0x16] | (buf[0x17] << 8), buf[0x18] | (buf[0x19] << 8), 1);
|
|
if (attr & (16 | 8)) {
|
|
int nextblock, cluster;
|
|
nextblock = dataregion + (startcluster - 2) * sectorspercluster;
|
|
cluster = getcluster (z, startcluster, fatstart, fatbits);
|
|
if ((cluster < 0 || cluster >= 3) && nextblock != startblock) {
|
|
znnew = zvolume_adddir_abs (zv, &zai);
|
|
fatdirectory (z, zv, name2, nextblock, sectorspercluster * 512 / 32, sectorspercluster, fatstart, dataregion, fatbits);
|
|
while (cluster >= 3) {
|
|
nextblock = dataregion + (cluster - 2) * sectorspercluster;
|
|
fatdirectory (z, zv, name2, nextblock, sectorspercluster * 512 / 32, sectorspercluster, fatstart, dataregion, fatbits);
|
|
cluster = getcluster (z, cluster, fatstart, fatbits);
|
|
}
|
|
}
|
|
} else {
|
|
zai.size = size;
|
|
znnew = zvolume_addfile_abs (zv, &zai);
|
|
if (znnew) {
|
|
znnew->offset = startcluster;
|
|
}
|
|
}
|
|
|
|
xfree (fname);
|
|
}
|
|
}
|
|
|
|
struct zvolume *archive_directory_fat (struct zfile *z)
|
|
{
|
|
uae_u8 buf[512] = { 0 };
|
|
int fatbits = 12;
|
|
struct zvolume *zv;
|
|
int rootdir, reserved, sectorspercluster;
|
|
int numfats, sectorsperfat, rootentries;
|
|
int dataregion;
|
|
|
|
zfile_fseek (z, 0, SEEK_SET);
|
|
zfile_fread (buf, 1, 512, z);
|
|
|
|
if (!isfat (buf))
|
|
return NULL;
|
|
reserved = buf[0x0e] | (buf[0x0f] << 8);
|
|
numfats = buf[0x10];
|
|
sectorsperfat = buf[0x16] | (buf[0x17] << 8);
|
|
rootentries = buf[0x11] | (buf[0x12] << 8);
|
|
sectorspercluster = buf[0x0d];
|
|
rootdir = reserved + numfats * sectorsperfat;
|
|
dataregion = rootdir + rootentries * 32 / 512;
|
|
|
|
zv = zvolume_alloc (z, ArchiveFormatFAT, NULL, NULL);
|
|
fatdirectory (z, zv, _T(""), rootdir, rootentries, sectorspercluster, reserved, dataregion, fatbits);
|
|
zv->method = ArchiveFormatFAT;
|
|
return zv;
|
|
}
|
|
|
|
static struct zfile *archive_access_fat (struct znode *zn)
|
|
{
|
|
uae_u8 buf[512] = { 0 };
|
|
int fatbits = 12;
|
|
uae_s64 size = zn->size;
|
|
struct zfile *sz, *dz;
|
|
int rootdir, reserved, sectorspercluster;
|
|
int numfats, sectorsperfat, rootentries;
|
|
int dataregion, cluster;
|
|
uae_s64 offset;
|
|
|
|
sz = zn->volume->archive;
|
|
|
|
zfile_fseek (sz, 0, SEEK_SET);
|
|
zfile_fread (buf, 1, 512, sz);
|
|
|
|
if (!isfat (buf))
|
|
return NULL;
|
|
reserved = buf[0x0e] | (buf[0x0f] << 8);
|
|
numfats = buf[0x10];
|
|
sectorsperfat = buf[0x16] | (buf[0x17] << 8);
|
|
rootentries = buf[0x11] | (buf[0x12] << 8);
|
|
sectorspercluster = buf[0x0d];
|
|
rootdir = reserved + numfats * sectorsperfat;
|
|
dataregion = rootdir + rootentries * 32 / 512;
|
|
|
|
dz = zfile_fopen_empty (sz, zn->fullname, size);
|
|
if (!dz)
|
|
return NULL;
|
|
|
|
offset = 0;
|
|
cluster = zn->offset;
|
|
while (size && cluster >= 2) {
|
|
uae_s64 left = size > sectorspercluster * 512 ? sectorspercluster * 512 : size;
|
|
int sector = dataregion + (cluster - 2) * sectorspercluster;
|
|
zfile_fseek (sz, sector * 512, SEEK_SET);
|
|
zfile_fread (dz->data + offset, 1, left, sz);
|
|
size -= left;
|
|
offset += left;
|
|
cluster = getcluster (sz, cluster, reserved, fatbits);
|
|
}
|
|
|
|
return dz;
|
|
}
|
|
|
|
void archive_access_close (void *handle, unsigned int id)
|
|
{
|
|
switch (id)
|
|
{
|
|
#ifdef A_ZIP
|
|
case ArchiveFormatZIP:
|
|
archive_close_zip (handle);
|
|
break;
|
|
#endif
|
|
#ifdef A_7Z
|
|
case ArchiveFormat7Zip:
|
|
archive_close_7z (handle);
|
|
break;
|
|
#endif
|
|
#ifdef A_RAR
|
|
case ArchiveFormatRAR:
|
|
archive_close_rar (handle);
|
|
break;
|
|
#endif
|
|
#ifdef A_LHA
|
|
case ArchiveFormatLHA:
|
|
break;
|
|
#endif
|
|
case ArchiveFormatADF:
|
|
archive_close_adf (handle);
|
|
break;
|
|
case ArchiveFormatTAR:
|
|
archive_close_tar (handle);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static struct zfile *archive_access_dir (struct znode *zn)
|
|
{
|
|
return zfile_fopen (zn->fullname, _T("rb"), 0);
|
|
}
|
|
|
|
struct zfile *archive_unpackzfile (struct zfile *zf)
|
|
{
|
|
struct zfile *zout = NULL;
|
|
if (!zf->archiveparent)
|
|
return NULL;
|
|
zf->datasize = zf->size;
|
|
switch (zf->archiveid)
|
|
{
|
|
#ifdef A_ZIP
|
|
case ArchiveFormatZIP:
|
|
zout = archive_unpack_zip (zf);
|
|
break;
|
|
#endif
|
|
}
|
|
zfile_fclose (zf->archiveparent);
|
|
zf->archiveparent = NULL;
|
|
zf->archiveid = 0;
|
|
return NULL;
|
|
}
|
|
|
|
struct zfile *archive_getzfile (struct znode *zn, unsigned int id, int flags)
|
|
{
|
|
struct zfile *zf = NULL;
|
|
|
|
switch (id)
|
|
{
|
|
#ifdef A_ZIP
|
|
case ArchiveFormatZIP:
|
|
zf = archive_access_zip (zn, flags);
|
|
break;
|
|
#endif
|
|
#ifdef A_7Z
|
|
case ArchiveFormat7Zip:
|
|
zf = archive_access_7z (zn);
|
|
break;
|
|
#endif
|
|
#ifdef A_RAR
|
|
case ArchiveFormatRAR:
|
|
zf = archive_access_rar (zn);
|
|
break;
|
|
#endif
|
|
#ifdef A_LHA
|
|
case ArchiveFormatLHA:
|
|
zf = archive_access_lha (zn);
|
|
break;
|
|
#endif
|
|
#ifdef A_LZX
|
|
case ArchiveFormatLZX:
|
|
zf = archive_access_lzx (zn);
|
|
break;
|
|
#endif
|
|
case ArchiveFormatPLAIN:
|
|
zf = archive_access_plain (zn);
|
|
break;
|
|
case ArchiveFormatADF:
|
|
zf = archive_access_adf (zn);
|
|
break;
|
|
case ArchiveFormatRDB:
|
|
zf = archive_access_rdb (zn);
|
|
break;
|
|
case ArchiveFormatFAT:
|
|
zf = archive_access_fat (zn);
|
|
break;
|
|
case ArchiveFormatDIR:
|
|
zf = archive_access_dir (zn);
|
|
break;
|
|
case ArchiveFormatTAR:
|
|
zf = archive_access_tar (zn);
|
|
break;
|
|
}
|
|
if (zf) {
|
|
zf->archiveid = id;
|
|
zfile_fseek (zf, 0, SEEK_SET);
|
|
}
|
|
return zf;
|
|
}
|