steed/src/steed/unpack.c

403 lines
8.7 KiB
C

#ifdef _USE_UNPACK
/*
miniunz.c
Version 1.1, February 14h, 2010
sample part of the MiniZip project - ( http://www.winimage.com/zLibDll/minizip.html )
Copyright (C) 1998-2010 Gilles Vollant (minizip) ( http://www.winimage.com/zLibDll/minizip.html )
Modifications of Unzip for Zip64
Copyright (C) 2007-2008 Even Rouault
Modifications for Zip64 support on both zip and unzip
Copyright (C) 2009-2010 Mathias Svensson ( http://result42.com )
*/
#ifndef _WIN32
#ifndef __USE_FILE_OFFSET64
#define __USE_FILE_OFFSET64
#endif
#ifndef __USE_LARGEFILE64
#define __USE_LARGEFILE64
#endif
#ifndef _LARGEFILE64_SOURCE
#define _LARGEFILE64_SOURCE
#endif
#ifndef _FILE_OFFSET_BIT
#define _FILE_OFFSET_BIT 64
#endif
#endif
#include "externals.h"
#ifdef unix
# include <unistd.h>
#ifndef S60
# include <utime.h>
#endif
#else
# include <direct.h>
# include <io.h>
#endif
#include "unzip.h"
#include "util.h"
#define CASESENSITIVITY (0)
#define WRITEBUFFERSIZE (8192)
#define MAXFILENAME (256)
#ifdef _WIN32
#define USEWIN32IOAPI
#include "iowin32.h"
#endif
char zip_game_dirname[PATH_MAX];
/* change_file_date : change the date/time of a file
filename : the filename of the file where date/time must be modified
dosdate : the new date at the MSDos format (4 bytes)
tmu_date : the SAME new date at the tm_unz format */
static void change_file_date(filename, dosdate, tmu_date)
const char *filename;
uLong dosdate;
tm_unz tmu_date;
{
#ifdef _WIN32
HANDLE hFile;
FILETIME ftm, ftLocal, ftCreate, ftLastAcc, ftLastWrite;
hFile = CreateFileA(filename, GENERIC_READ | GENERIC_WRITE,
0, NULL, OPEN_EXISTING, 0, NULL);
GetFileTime(hFile, &ftCreate, &ftLastAcc, &ftLastWrite);
DosDateTimeToFileTime((WORD) (dosdate >> 16), (WORD) dosdate, &ftLocal);
LocalFileTimeToFileTime(&ftLocal, &ftm);
SetFileTime(hFile, &ftm, &ftLastAcc, &ftm);
CloseHandle(hFile);
#else
#ifdef unix
#ifndef S60
struct utimbuf ut;
struct tm newdate;
newdate.tm_sec = tmu_date.tm_sec;
newdate.tm_min = tmu_date.tm_min;
newdate.tm_hour = tmu_date.tm_hour;
newdate.tm_mday = tmu_date.tm_mday;
newdate.tm_mon = tmu_date.tm_mon;
if (tmu_date.tm_year > 1900)
newdate.tm_year = tmu_date.tm_year - 1900;
else
newdate.tm_year = tmu_date.tm_year;
newdate.tm_isdst = -1;
ut.actime = ut.modtime = mktime(&newdate);
utime(filename, &ut);
#endif
#endif
#endif
}
/* mymkdir and change_file_date are not 100 % portable
As I don't know well Unix, I wait feedback for the unix portion */
static int mymkdir(dirname)
const char *dirname;
{
int ret = 0;
#ifdef _WIN32
ret = _mkdir(dirname);
#else
#ifdef unix
ret = mkdir(dirname, 0775);
#endif
#endif
return ret;
}
static int makedir(newdir)
char *newdir;
{
char *buffer;
char *p;
int len = (int)strlen(newdir);
if (len <= 0)
return 0;
buffer = (char *)malloc(len + 1);
if (buffer == NULL) {
fprintf(stderr, "Error allocating memory\n");
return UNZ_INTERNALERROR;
}
strcpy(buffer, newdir);
if (buffer[len - 1] == '/') {
buffer[len - 1] = '\0';
}
if (mymkdir(buffer) == 0) {
free(buffer);
return 1;
}
p = buffer + 1;
while (1) {
char hold;
while (*p && *p != '\\' && *p != '/')
p++;
hold = *p;
*p = 0;
if ((mymkdir(buffer) == -1) && (errno == ENOENT)) {
fprintf(stderr, "couldn't create directory %s\n", buffer);
free(buffer);
return 0;
}
if (hold == 0)
break;
*p++ = hold;
}
free(buffer);
return 1;
}
static int do_extract_currentfile(uf, password)
unzFile uf;
const char *password;
{
char filename_inzip[256];
char *filename_withoutpath;
char *p;
int err = UNZ_OK;
FILE *fout = NULL;
void *buf;
uInt size_buf;
unz_file_info64 file_info;
err =
unzGetCurrentFileInfo64(uf, &file_info, filename_inzip,
sizeof(filename_inzip), NULL, 0, NULL, 0);
if (err != UNZ_OK) {
fprintf(stderr, "error %d with zipfile in unzGetCurrentFileInfo\n", err);
return err;
}
size_buf = WRITEBUFFERSIZE;
buf = (void *)malloc(size_buf);
if (buf == NULL) {
fprintf(stderr, "Error allocating memory\n");
return UNZ_INTERNALERROR;
}
p = filename_withoutpath = filename_inzip;
while ((*p) != '\0') {
if (((*p) == '/') || ((*p) == '\\'))
filename_withoutpath = p + 1;
p++;
}
if ((*filename_withoutpath) == '\0') {
if (zip_game_dirname[0] && strncmp(zip_game_dirname,
filename_inzip, strlen(zip_game_dirname))) {
err = -1;
fprintf(stderr, "Too many dirs in zip...\n");
goto out;
}
fprintf(stderr, "creating directory: %s\n", filename_inzip);
mymkdir(filename_inzip);
if (!*zip_game_dirname)
strcpy(zip_game_dirname, filename_inzip);
} else {
const char *write_filename;
int skip = 0;
write_filename = filename_inzip;
err = unzOpenCurrentFilePassword(uf, password);
if (err != UNZ_OK) {
fprintf
(stderr, "error %d with zipfile in unzOpenCurrentFilePassword\n",
err);
goto out;
}
if (skip == 0) {
fout = fopen64(write_filename, "wb");
/* some zipfile don't contain directory alone before file */
if ((fout == NULL)
&& (filename_withoutpath != (char *)filename_inzip)) {
char c = *(filename_withoutpath - 1);
*(filename_withoutpath - 1) = '\0';
makedir(write_filename);
*(filename_withoutpath - 1) = c;
fout = fopen64(write_filename, "wb");
}
if (fout == NULL) {
fprintf(stderr, "error opening %s\n", write_filename);
}
}
if ((filename_withoutpath != (char *)filename_inzip) &&
!*zip_game_dirname) {
strcpy(zip_game_dirname, filename_inzip);
zip_game_dirname[strcspn(zip_game_dirname,"/\\")] = 0;
}
if (fout != NULL) {
fprintf(stderr, " extracting: %s\n", write_filename);
do {
err = unzReadCurrentFile(uf, buf, size_buf);
if (err < 0) {
fprintf
(stderr, "error %d with zipfile in unzReadCurrentFile\n",
err);
break;
}
if (err > 0)
if (fwrite(buf, err, 1, fout) != 1) {
fprintf
(stderr, "error in writing extracted file\n");
err = UNZ_ERRNO;
break;
}
}
while (err > 0);
if (fout)
fclose(fout);
if (err == 0)
change_file_date(write_filename,
file_info.dosDate,
file_info.tmu_date);
}
if (err == UNZ_OK) {
err = unzCloseCurrentFile(uf);
if (err != UNZ_OK) {
fprintf
(stderr, "error %d with zipfile in unzCloseCurrentFile\n",
err);
}
} else
unzCloseCurrentFile(uf); /* don't lose the error */
if (!*zip_game_dirname) {
err = -1;
fprintf(stderr, "No dir in zip...\n");
if (idf_magic(write_filename)) {
fprintf(stderr, "Idf unpacked: %s\n", write_filename);
strcpy(zip_game_dirname, write_filename);
err = 0;
} else
unlink(write_filename);
goto out;
}
}
if (*zip_game_dirname) {
int s = strlen(zip_game_dirname);
unix_path(zip_game_dirname);
if (s && (zip_game_dirname[s - 1] == '/'))
s --;
zip_game_dirname[s] = 0;
}
out:
free(buf);
return err;
}
static int do_extract(uf, password)
unzFile uf;
const char *password;
{
uLong i;
unz_global_info64 gi;
int err;
err = unzGetGlobalInfo64(uf, &gi);
if (err != UNZ_OK)
fprintf(stderr, "error %d with zipfile in unzGetGlobalInfo \n", err);
for (i = 0; i < gi.number_entry; i++) {
if (do_extract_currentfile(uf, password) != UNZ_OK)
return -1;
if ((i + 1) < gi.number_entry) {
err = unzGoToNextFile(uf);
if (err != UNZ_OK) {
fprintf
(stderr, "error %d with zipfile in unzGoToNextFile\n",
err);
return -1;
}
}
}
return 0;
}
int unpack(const char *zipfilename, const char *dirname)
{
char game_cwd[PATH_MAX];
char filename_try[MAXFILENAME + 16] = "";
int ret_value = 0;
getcwd(game_cwd, sizeof(game_cwd));
unzFile uf = NULL;
zip_game_dirname[0] = 0;
# ifdef USEWIN32IOAPI
zlib_filefunc64_def ffunc;
# endif
strncpy(filename_try, zipfilename, MAXFILENAME - 1);
/* strncpy doesnt append the trailing NULL, of the string is too long. */
filename_try[MAXFILENAME] = '\0';
# ifdef USEWIN32IOAPI
fill_win32_filefunc64A(&ffunc);
uf = unzOpen2_64(zipfilename, &ffunc);
# else
uf = unzOpen64(zipfilename);
# endif
if (uf == NULL) {
strcat(filename_try, ".zip");
# ifdef USEWIN32IOAPI
uf = unzOpen2_64(filename_try, &ffunc);
# else
uf = unzOpen64(filename_try);
# endif
}
if (uf == NULL) {
fprintf(stderr, "Cannot open %s or %s.zip\n", zipfilename,
zipfilename);
return -1;
}
fprintf(stderr, "%s opened\n", filename_try);
#ifdef _WIN32
if (dirname && _chdir(dirname))
#else
if (dirname && chdir(dirname))
#endif
{
ret_value = -1;
fprintf(stderr, "Error changing dir to %s, aborting\n", dirname);
goto out;
}
ret_value = do_extract(uf, NULL);
out:
unzClose(uf);
#ifdef _WIN32
if (dirname)
_chdir(game_cwd);
#else
if (dirname)
chdir(game_cwd);
#endif
return ret_value;
}
#endif