snes9xgx/source/gcunzip.cpp

532 lines
12 KiB
C++
Raw Normal View History

/****************************************************************************
2010-01-27 23:20:37 +01:00
* Snes9x Nintendo Wii/Gamecube Port
*
* softdev July 2006
2008-09-23 06:13:33 +02:00
* Michniewski 2008
2021-01-06 21:13:32 +01:00
* Tantric 2008-2021
*
* gcunzip.cpp
*
* File unzip routines
***************************************************************************/
#include <gccore.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <zlib.h>
#include <sys/stat.h>
2008-09-09 19:36:48 +02:00
2010-03-22 00:43:54 +01:00
#include "snes9xgx.h"
#include "fileop.h"
2009-03-11 18:28:37 +01:00
#include "filebrowser.h"
#include "menu.h"
#include "gcunzip.h"
2010-03-22 00:43:54 +01:00
extern "C" {
#include "utils/sz/7zCrc.h"
#include "utils/sz/7zIn.h"
#include "utils/sz/7zExtract.h"
}
#define ZIPCHUNK 2048
/*
* Zip file header definition
*/
typedef struct
{
unsigned int zipid __attribute__ ((__packed__)); // 0x04034b50
unsigned short zipversion __attribute__ ((__packed__));
unsigned short zipflags __attribute__ ((__packed__));
unsigned short compressionMethod __attribute__ ((__packed__));
unsigned short lastmodtime __attribute__ ((__packed__));
unsigned short lastmoddate __attribute__ ((__packed__));
unsigned int crc32 __attribute__ ((__packed__));
unsigned int compressedSize __attribute__ ((__packed__));
unsigned int uncompressedSize __attribute__ ((__packed__));
unsigned short filenameLength __attribute__ ((__packed__));
unsigned short extraDataLength __attribute__ ((__packed__));
}
PKZIPHEADER;
/*
* Zip files are stored little endian
* Support functions for short and int types
*/
static u32
FLIP32 (u32 b)
{
2008-08-12 05:25:16 +02:00
unsigned int c;
2008-08-12 05:25:16 +02:00
c = (b & 0xff000000) >> 24;
c |= (b & 0xff0000) >> 8;
c |= (b & 0xff00) << 8;
c |= (b & 0xff) << 24;
2008-08-12 05:25:16 +02:00
return c;
}
static u16
FLIP16 (u16 b)
{
2008-08-12 05:25:16 +02:00
u16 c;
2008-08-12 05:25:16 +02:00
c = (b & 0xff00) >> 8;
c |= (b & 0xff) << 8;
2008-08-12 05:25:16 +02:00
return c;
}
/****************************************************************************
* IsZipFile
*
* Returns TRUE when 0x504b0304 is first four characters of buffer
***************************************************************************/
int
IsZipFile (char *buffer)
{
2011-10-27 06:12:14 +02:00
unsigned int *check = (unsigned int *) buffer;
if (check[0] == 0x504b0304)
2008-08-12 05:25:16 +02:00
return 1;
2008-08-12 05:25:16 +02:00
return 0;
}
2008-10-14 11:21:34 +02:00
/*****************************************************************************
* UnZipBuffer
******************************************************************************/
2008-08-12 05:25:16 +02:00
2009-10-03 21:46:57 +02:00
size_t
UnZipBuffer (unsigned char *outbuffer, size_t buffersize)
{
2008-08-12 05:25:16 +02:00
PKZIPHEADER pkzip;
2009-10-03 21:46:57 +02:00
size_t zipoffset = 0;
size_t zipchunk = 0;
2008-08-12 05:25:16 +02:00
char out[ZIPCHUNK];
z_stream zs;
int res;
2009-10-03 21:46:57 +02:00
size_t bufferoffset = 0;
size_t have = 0;
2008-08-14 00:44:59 +02:00
char readbuffer[ZIPCHUNK];
2009-10-03 21:46:57 +02:00
size_t sizeread = 0;
2008-10-14 11:21:34 +02:00
// Read Zip Header
fseek(file, 0, SEEK_SET);
sizeread = fread (readbuffer, 1, ZIPCHUNK, file);
2008-12-18 19:36:30 +01:00
if(sizeread <= 0)
return 0;
/*** Copy PKZip header to local, used as info ***/
2008-08-12 05:25:16 +02:00
memcpy (&pkzip, readbuffer, sizeof (PKZIPHEADER));
2008-08-12 05:25:16 +02:00
pkzip.uncompressedSize = FLIP32 (pkzip.uncompressedSize);
2008-08-07 07:19:17 +02:00
if(pkzip.uncompressedSize > buffersize) {
return 0;
}
2008-12-18 19:36:30 +01:00
ShowProgress ("Loading...", 0, pkzip.uncompressedSize);
/*** Prepare the zip stream ***/
2008-08-12 05:25:16 +02:00
memset (&zs, 0, sizeof (z_stream));
zs.zalloc = Z_NULL;
zs.zfree = Z_NULL;
zs.opaque = Z_NULL;
zs.avail_in = 0;
zs.next_in = Z_NULL;
res = inflateInit2 (&zs, -MAX_WBITS);
2008-08-12 05:25:16 +02:00
if (res != Z_OK)
2009-03-11 18:28:37 +01:00
goto done;
/*** Set ZipChunk for first pass ***/
2008-08-12 05:25:16 +02:00
zipoffset =
(sizeof (PKZIPHEADER) + FLIP16 (pkzip.filenameLength) +
FLIP16 (pkzip.extraDataLength));
zipchunk = ZIPCHUNK - zipoffset;
/*** Now do it! ***/
2008-08-12 05:25:16 +02:00
do
{
2008-08-12 05:25:16 +02:00
zs.avail_in = zipchunk;
zs.next_in = (Bytef *) & readbuffer[zipoffset];
2008-08-12 05:25:16 +02:00
/*** Now inflate until input buffer is exhausted ***/
do
{
zs.avail_out = ZIPCHUNK;
zs.next_out = (Bytef *) & out;
res = inflate (&zs, Z_NO_FLUSH);
if (res == Z_MEM_ERROR)
{
2009-03-11 18:28:37 +01:00
goto done;
2008-08-12 05:25:16 +02:00
}
have = ZIPCHUNK - zs.avail_out;
if (have)
{
2008-08-14 00:44:59 +02:00
/*** Copy to normal block buffer ***/
memcpy (&outbuffer[bufferoffset], &out, have);
bufferoffset += have;
2008-08-12 05:25:16 +02:00
}
}
while (zs.avail_out == 0);
2008-10-14 11:21:34 +02:00
// Readup the next 2k block
2008-08-12 05:25:16 +02:00
zipoffset = 0;
zipchunk = ZIPCHUNK;
sizeread = fread (readbuffer, 1, ZIPCHUNK, file);
2008-12-18 19:36:30 +01:00
if(sizeread <= 0)
2009-03-11 18:28:37 +01:00
goto done; // read failure
2008-12-18 19:36:30 +01:00
ShowProgress ("Loading...", bufferoffset, pkzip.uncompressedSize);
2008-08-12 05:25:16 +02:00
}
while (res != Z_STREAM_END);
2009-03-11 18:28:37 +01:00
done:
2008-08-12 05:25:16 +02:00
inflateEnd (&zs);
2009-03-11 18:28:37 +01:00
CancelAction();
2008-08-12 05:25:16 +02:00
if (res == Z_STREAM_END)
2009-03-11 18:28:37 +01:00
return pkzip.uncompressedSize;
else
return 0;
}
/****************************************************************************
2008-10-14 11:21:34 +02:00
* GetFirstZipFilename
*
* Returns the filename of the first file in the zipped archive
* The idea here is to do the least amount of work required
***************************************************************************/
char *
2009-10-02 00:35:12 +02:00
GetFirstZipFilename ()
{
2008-10-14 11:21:34 +02:00
char * firstFilename = NULL;
char tempbuffer[ZIPCHUNK];
2008-11-12 08:50:39 +01:00
char filepath[1024];
2009-10-02 00:35:12 +02:00
if(!MakeFilePath(filepath, FILE_ROM))
2008-11-12 08:50:39 +01:00
return NULL;
2008-11-12 08:50:39 +01:00
// read start of ZIP
if(LoadFile (tempbuffer, filepath, ZIPCHUNK, ZIPCHUNK, NOTSILENT) < 35)
return NULL;
tempbuffer[28] = 0; // truncate - filename length is 2 bytes long (bytes 26-27)
int namelength = tempbuffer[26]; // filename length starts 26 bytes in
if(namelength < 0 || namelength > 200) // filename is not a reasonable length
{
ErrorPrompt("Error - Invalid ZIP file!");
return NULL;
}
firstFilename = &tempbuffer[30]; // first filename of a ZIP starts 31 bytes in
firstFilename[namelength] = 0; // truncate at filename length
return strdup(firstFilename);
}
2008-10-14 11:21:34 +02:00
/****************************************************************************
* 7z functions
***************************************************************************/
typedef struct _SzFileInStream
{
ISzInStream InStream;
u64 offset; // offset of the file
unsigned int len; // length of the file
u64 pos; // current position of the file pointer
} SzFileInStream;
// 7zip error list
2009-07-09 23:51:19 +02:00
static char szerrormsg[][100] = {
"File is corrupt.", // 7z: Data error
"Archive contains too many files.", // 7z: Out of memory
"File is corrupt (CRC mismatch).", // 7z: CRC Error
"File uses unsupported compression settings.", // 7z: Not implemented
"File is corrupt.", // 7z: Fail
"Failed to read file data.", // 7z: Data read failure
"File is corrupt.", // 7z: Archive error
2009-07-09 23:51:19 +02:00
"File uses too high of compression settings (dictionary size is too large).", // 7z: Dictionary too large
2008-10-14 11:21:34 +02:00
};
static SZ_RESULT SzRes;
static SzFileInStream SzArchiveStream;
static CArchiveDatabaseEx SzDb;
static ISzAlloc SzAllocImp;
static ISzAlloc SzAllocTempImp;
static UInt32 SzBlockIndex = 0xFFFFFFFF;
static size_t SzBufferSize;
static size_t SzOffset;
static size_t SzOutSizeProcessed;
static CFileItem *SzF;
2008-10-14 11:21:34 +02:00
static char sz_buffer[2048];
static int szMethod = 0;
2008-10-14 11:21:34 +02:00
/****************************************************************************
* Is7ZipFile
*
* Returns 1 when 7z signature is found
****************************************************************************/
int
Is7ZipFile (char *buffer)
{
// 7z signature
static Byte Signature[6] = {'7', 'z', 0xBC, 0xAF, 0x27, 0x1C};
int i;
for(i = 0; i < 6; i++)
if(buffer[i] != Signature[i])
return 0;
return 1; // 7z archive found
}
// display an error message
static void SzDisplayError(SZ_RESULT res)
2008-10-14 11:21:34 +02:00
{
char msg[1024];
sprintf(msg, "7z decompression failed: %s", szerrormsg[(res - 1)]);
ErrorPrompt(msg);
2008-10-14 11:21:34 +02:00
}
// function used by the 7zip SDK to read data from SD/USB/DVD/SMB
static SZ_RESULT SzFileReadImp(void *object, void **buffer, size_t maxRequiredSize, size_t *processedSize)
2008-10-14 11:21:34 +02:00
{
2009-12-01 09:33:41 +01:00
size_t sizeread = 0;
if(maxRequiredSize == 0)
return SZ_OK;
2008-12-18 19:36:30 +01:00
2008-10-15 08:35:14 +02:00
// the void* object is a SzFileInStream
SzFileInStream *s = (SzFileInStream *) object;
2008-10-14 11:21:34 +02:00
2008-10-15 08:35:14 +02:00
if (maxRequiredSize > 2048)
2008-10-14 11:21:34 +02:00
maxRequiredSize = 2048;
2008-10-15 08:35:14 +02:00
// read data
sizeread = fread(sz_buffer, 1, maxRequiredSize, file);
2008-10-14 11:21:34 +02:00
2009-12-01 09:33:41 +01:00
if(sizeread <= 0)
2008-12-18 19:36:30 +01:00
return SZE_FAILREAD;
2008-10-15 08:35:14 +02:00
*buffer = sz_buffer;
2009-12-01 09:33:41 +01:00
*processedSize = sizeread;
s->pos += sizeread;
2008-10-15 08:35:14 +02:00
2009-12-01 09:33:41 +01:00
if(sizeread > 1024) // only show progress for large reads
2008-10-15 08:35:14 +02:00
// this isn't quite right, but oh well
ShowProgress ("Loading...", s->pos, browserList[browser.selIndex].length);
2008-10-14 11:21:34 +02:00
2008-10-15 08:35:14 +02:00
return SZ_OK;
2008-10-14 11:21:34 +02:00
}
// function used by the 7zip SDK to change the filepointer
static SZ_RESULT SzFileSeekImp(void *object, CFileSize pos)
2008-10-14 11:21:34 +02:00
{
2008-10-15 08:35:14 +02:00
// the void* object is a SzFileInStream
SzFileInStream *s = (SzFileInStream *) object;
// check if the 7z SDK wants to move the pointer to somewhere after the EOF
if (pos >= s->len)
return SZE_FAIL;
// save new position and return
2009-12-01 09:33:41 +01:00
if(fseek(file, (long)pos, SEEK_SET) != 0)
return SZE_FAIL;
2008-10-15 08:35:14 +02:00
s->pos = pos;
return SZ_OK;
2008-10-14 11:21:34 +02:00
}
2009-10-03 21:46:57 +02:00
/****************************************************************************
* SzClose
*
* Closes a 7z file
***************************************************************************/
void SzClose()
{
if(SzDb.Database.NumFiles > 0)
SzArDbExFree(&SzDb, SzAllocImp.Free);
}
2008-10-14 11:21:34 +02:00
/****************************************************************************
* SzParse
*
* Opens a 7z file, and parses it
* It parses the entire 7z for full browsing capability
2008-10-14 11:21:34 +02:00
***************************************************************************/
2009-10-02 00:35:12 +02:00
int SzParse(char * filepath)
2008-10-14 11:21:34 +02:00
{
if(!filepath)
return 0;
2009-10-02 00:35:12 +02:00
int device;
struct stat filestat;
if(stat(filepath, &filestat) < 0)
return 0;
unsigned int filelen = filestat.st_size;
if(!FindDevice(filepath, &device) || !filelen)
2009-10-02 00:35:12 +02:00
return 0;
2008-10-14 11:21:34 +02:00
int nbfiles = 0;
// setup archive stream
SzArchiveStream.offset = 0;
SzArchiveStream.len = filelen;
2008-10-14 11:21:34 +02:00
SzArchiveStream.pos = 0;
// open file
file = fopen (filepath, "rb");
if(!file)
return 0;
2008-10-14 11:21:34 +02:00
2009-10-02 00:35:12 +02:00
// set szMethod to current chosen load device
szMethod = device;
2008-10-14 11:21:34 +02:00
2008-12-18 19:36:30 +01:00
// set handler functions for reading data from SD/USB/SMB/DVD
2008-10-14 11:21:34 +02:00
SzArchiveStream.InStream.Read = SzFileReadImp;
SzArchiveStream.InStream.Seek = SzFileSeekImp;
// set default 7Zip SDK handlers for allocation and freeing memory
SzAllocImp.Alloc = SzAlloc;
SzAllocImp.Free = SzFree;
SzAllocTempImp.Alloc = SzAllocTemp;
SzAllocTempImp.Free = SzFreeTemp;
// prepare CRC and 7Zip database structures
InitCrcTable();
SzArDbExInit(&SzDb);
// open the archive
SzRes = SzArchiveOpen(&SzArchiveStream.InStream, &SzDb, &SzAllocImp,
&SzAllocTempImp);
if (SzRes != SZ_OK)
{
SzDisplayError(SzRes);
// free memory used by the 7z SDK
SzClose();
}
else // archive opened successfully
{
if(SzDb.Database.NumFiles > 0)
{
// Parses the 7z into a full file listing
2010-12-05 22:48:48 +01:00
HaltParseThread(); // halt parsing
ResetBrowser(); // reset browser
2008-10-14 11:21:34 +02:00
2008-10-15 20:29:01 +02:00
// add '..' folder in case the user wants exit the 7z
2009-10-03 21:46:57 +02:00
AddBrowserEntry();
sprintf(browserList[0].filename, "..");
2009-07-09 23:51:19 +02:00
sprintf(browserList[0].displayname, "Up One Level");
browserList[0].isdir = 1;
2009-10-03 21:46:57 +02:00
browserList[0].icon = ICON_FOLDER;
2008-10-14 11:21:34 +02:00
// get contents and parse them into file list structure
unsigned int SzI, SzJ;
SzJ = 1;
for (SzI = 0; SzI < SzDb.Database.NumFiles; SzI++)
{
SzF = SzDb.Database.Files + SzI;
// skip directories
if (SzF->IsDirectory)
continue;
2009-10-03 21:46:57 +02:00
if(!AddBrowserEntry())
{
ResetBrowser();
2009-03-11 18:28:37 +01:00
ErrorPrompt("Out of memory: too many files!");
2009-10-03 21:46:57 +02:00
SzClose();
SzJ = 0;
2008-10-14 11:21:34 +02:00
break;
}
2008-10-14 11:21:34 +02:00
// parse information about this file to the file list structure
2011-03-20 18:17:51 +01:00
snprintf(browserList[SzJ].filename, MAXJOLIET, "%s", SzF->Name);
2009-10-03 21:46:57 +02:00
StripExt(browserList[SzJ].displayname, browserList[SzJ].filename);
char* strPos = strstr(browserList[SzJ].displayname, szname);
if(strPos)
{
snprintf(browserList[SzJ].displayname, MAXJOLIET, "%s", strPos + strlen(szname));
}
browserList[SzJ].length = SzF->Size; // filesize
browserList[SzJ].isdir = 0; // only files will be displayed (-> no flags)
2009-10-03 21:46:57 +02:00
browserList[SzJ].filenum = SzI; // the extraction function identifies the file with this number
2008-10-14 11:21:34 +02:00
SzJ++;
}
nbfiles = SzJ;
}
else
{
2009-10-03 21:46:57 +02:00
SzClose();
2008-10-14 11:21:34 +02:00
}
}
2009-07-09 23:51:19 +02:00
CancelAction();
2008-10-14 11:21:34 +02:00
// close file
fclose(file);
2008-10-14 11:21:34 +02:00
return nbfiles;
}
/****************************************************************************
* SzExtractFile
*
* Extracts the given file # into the buffer specified
* Must parse the 7z BEFORE running this function
***************************************************************************/
2009-10-03 21:46:57 +02:00
size_t SzExtractFile(int i, unsigned char *buffer)
2008-10-14 11:21:34 +02:00
{
2008-10-15 08:35:14 +02:00
// prepare some variables
SzBlockIndex = 0xFFFFFFFF;
SzOffset = 0;
// Unzip the file
SzRes = SzExtract2(
2008-12-18 19:36:30 +01:00
&SzArchiveStream.InStream,
&SzDb,
i, // index of file
&SzBlockIndex, // index of solid block
&buffer,
&SzBufferSize,
&SzOffset, // offset of stream for required file in *outBuffer
&SzOutSizeProcessed, // size of file in *outBuffer
&SzAllocImp,
&SzAllocTempImp);
2008-10-15 08:35:14 +02:00
// close 7Zip archive and free memory
2008-10-14 11:21:34 +02:00
SzClose();
2009-03-11 18:28:37 +01:00
CancelAction();
2008-10-15 08:35:14 +02:00
// check for errors
if(SzRes != SZ_OK)
{
// display error message
SzDisplayError(SzRes);
return 0;
}
else
{
return SzOutSizeProcessed;
}
2008-10-14 11:21:34 +02:00
}