Files
2026-04-30 00:13:55 +02:00

594 lines
13 KiB
C++

// Unicode NSIS support by Gringoloco023, http://portableapps.com/node/21879, Februari 6 2010
/*
UnZip Plug-in for NSIS
Written by Saivert
Credits:
- Based on code in NSIS Zip2Exe
portions Copyright © 1999-2001 Miguel Garrido (mgarrido01@hotmail.com)
- Uses ZLIB - Copyright © Mark Adler
- ZIP format routines - Copyright (C) 1998 Gilles Vollant
Even though this project is an NSIS Plug-in, it also
exports a function that can be used by any applications.
The exported function is "appextract"
*/
//#include "AggressiveOptimize.h"
#include <windows.h>
#include <stdio.h>
#include <commctrl.h>
#include "..\ExDLL\exdll.h"
//#include "exdll.h"
extern "C" {
#include "zlib/unzip.h"
};
//Strings used all over the place
TCHAR szSuccess[] = {_T('s'),_T('u'),_T('c'),_T('c'),_T('e'),_T('s'),_T('s'),_T('\0')};
TCHAR szFile[] = {_T('/'),_T('f'),_T('i'),_T('l'),_T('e'),_T('\0')};
TCHAR szNoextractpath[] = {_T('/'),_T('n'),_T('o'),_T('e'),_T('x'),_T('t'),_T('r'),_T('a'),_T('c'),_T('t'),_T('p'),_T('a'),_T('t'),_T('h'),_T('\0')};
TCHAR szDefExtractText[] = {_T('E'),_T('x'),_T('t'),_T('r'),_T('a'),_T('c'),_T('t'),_T(':'),_T(' '),_T('%'),_T('f'),_T('\0')};
TCHAR g_extract_text[1024];
int g_extracting;
HWND g_hwndParent;
TCHAR tempzip_path[1024];
void internal_unzip(int);
CRITICAL_SECTION cs;
//Log stuff
HWND g_hwndList;
HWND g_hwndStatus;
void LogMessage(HWND, const TCHAR *, int = 0);
#ifdef UNICODE
char * _T2A(wchar_t *str);
wchar_t * _A2T(char *str);
#define _tfopen_s _wfopen_s
#else
#define _T2A(x) (x)
#define _A2T(x) (x)
#define _tfopen_s fopen_s
#endif
extern "C" BOOL WINAPI DllMain(HANDLE hInst, ULONG ul_reason_for_call, LPVOID lpReserved)
{
switch (ul_reason_for_call)
{
//case DLL_THREAD_ATTACH:
case DLL_PROCESS_ATTACH:
InitializeCriticalSection(&cs);
break;
//case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
DeleteCriticalSection(&cs);
break;
}
return TRUE;
}
void doMKDir(TCHAR *directory)
{
TCHAR *p, *p2;
TCHAR buf[MAX_PATH];
if (!*directory) return;
lstrcpy(buf,directory);
p=buf; while (*p) p++;
while (p >= buf && *p != _T('\\')) p--;
p2 = buf;
if (p2[1] == _T(':')) p2+=4;
else if (p2[0] == _T('\\') && p2[1] == _T('\\'))
{
p2+=2;
while (*p2 && *p2 != _T('\\')) p2++;
if (*p2) p2++;
while (*p2 && *p2 != _T('\\')) p2++;
if (*p2) p2++;
}
if (p >= p2)
{
*p=0;
doMKDir(buf);
}
CreateDirectory(directory,NULL);
}
//Based on inttosizestr from NSIS exehead project.
//Creates a string representing the size of a file,
//choosing the unit that's best suited.
static TCHAR * FormatDiskSize(TCHAR *str, unsigned long b)
{
TCHAR scale=_T('k');
TCHAR sh=30;
TCHAR s=0;
if (b <= (1024*1024) ) sh=10, scale=_T('k');
else if (b <= (1024*1024*1024)) sh=20, scale=_T('M');
else if (GetVersion()&0x80000000) s=_T('+'); //only display '+' on GB shown on Win95
if (b >= 1000) //represent everything above 1000 bytes as 0,xx kB
{
wsprintf(str, _T("%d.%d%d %cB%c"),
b>>sh, ((b*10)>>sh)%10, ((b*20)>>sh)%10, scale, s);
if (b < 1024)
wsprintf(str+_tcsclen(str), _T(" (%u byte%c)"), b, b == 1?0:_T('s'));
}
else
wsprintf(str, _T("%u byte%c"), b, b == 1?0:_T('s'));
return str;
}
/* A simple format parser
* Handles %f, %c and %u
* size of out must be twice as large as size of str
*/
static TCHAR * parse(const TCHAR *str, TCHAR *out, int outlen,
TCHAR *filename,
unsigned long compressed,
unsigned long uncompressed,
unsigned int nkb)
{
TCHAR s[32];
const TCHAR *p;
UINT i=0;
p=str-1;
memset(out, 0, outlen);
while (p++ && (*p != 0))
{
if (*p == _T('%'))
{
switch (*(p+1))
{
case _T('c'):
case _T('C'):
{
FormatDiskSize(s, compressed);
lstrcat(out, s);
i += lstrlen(s);
break;
}
case _T('u'):
case _T('U'):
{
FormatDiskSize(s, uncompressed);
lstrcat(out, s);
i += lstrlen(s);
break;
}
case _T('f'):
case _T('F'):
{
lstrcat(out, filename);
i += lstrlen(filename);
break;
}
case _T('p'):
case _T('P'):
{
unsigned int j;
j = uncompressed;
if (!j) j = 1;
wsprintf(s, _T("%d%%"), nkb*100/j);
lstrcat(out, s);
i += lstrlen(s);
break;
}
case _T('b'):
case _T('B'):
{
FormatDiskSize(s, nkb);
lstrcat(out, s);
i += lstrlen(s);
break;
}
default: out[i++] = *p;
}
++p;
} else {
out[i++] = *p;
}
}
return out;
}
extern "C" __declspec(dllexport)
void Unzip(HWND hwndParent, int string_size, TCHAR *variables, stack_t **stacktop)
{
EXDLL_INIT();
g_hwndParent = hwndParent;
internal_unzip(0);
}
extern "C" __declspec(dllexport)
void UnzipToLog(HWND hwndParent, int string_size, TCHAR *variables, stack_t **stacktop)
{
EXDLL_INIT();
g_hwndParent = hwndParent;
internal_unzip(1);
}
extern "C" __declspec(dllexport)
void UnzipToStack(HWND hwndParent, int string_size, TCHAR *variables, stack_t **stacktop)
{
EXDLL_INIT();
g_hwndParent = hwndParent;
internal_unzip(2);
}
// This function is to be used by rundll32.exe. Call it like this:
//
// rundll32.exe "c:\a path\nsisunz.dll",extract_RunDLL c:\path\zipfile.zip c:\outdir
//
// or to extract a single file, use this (concatenate the next two lines):
//
// rundll32.exe c:\path\nsisunz.dll,extract_RunDLL
// /file readme.txt "c:\a path\zipfile.zip" c:\outdir
//
// "/noextractpath" is implicitly used if "/file" is used.
// It mimics NSIS itself by setting up a stack and pushing the
// parameters from the function on the stack. I made it like this
// so I could directly reuse the code I already had written without modifications.
extern "C" __declspec(dllexport)
void extract_RunDLL(HINSTANCE hAppInstance, LPVOID unused, TCHAR *params)
{
TCHAR res[1024];
TCHAR temp[256];
TCHAR zipfile[MAX_PATH]={0,};
TCHAR destdir[MAX_PATH]={0,};
TCHAR file[MAX_PATH]={0,};
TCHAR *p = params;
TCHAR *s;
TCHAR q=0;
int next=0;
#define NEXT_FILE 1
#define NEXT_ZIP 2
#define NEXT_DEST 3
stack_t ownstacktop;
stack_t *pownstacktop = &ownstacktop;
ownstacktop.next = NULL;
ownstacktop.text[0] = NULL;
g_stringsize=1024;
g_stacktop=&pownstacktop;
g_variables=NULL;
//Parse the parameters
//While we do the parsing we use next as a clue to
//what comes next. This way I don't have to write a
//new function.
while (*p)
{
if (*p==_T('"')) q=_T('"'), p++; else q=_T(' '); //Do we search for a quote or a space?
s = temp;
while (*p && *p != q) *s++ = *p++; //Copy chars from params to zipfile
*s = 0; //Terminate string
if (next==NEXT_FILE) lstrcpy(file, temp), next=NEXT_ZIP;
else if (next==NEXT_ZIP) lstrcpy(zipfile, temp), next=NEXT_DEST;
else if (next==NEXT_DEST) lstrcpy(destdir, temp), next=0;
else if (!lstrcmpi(temp, szFile)) next=NEXT_FILE;
else lstrcpy(zipfile, temp), next=NEXT_DEST;
while (*p && *++p == _T(' '));
}
pushstring(destdir);
pushstring(zipfile);
if (file[0])
{
pushstring(file);
pushstring(szFile);
pushstring(szNoextractpath);
}
internal_unzip(0);
popstring(res);
if (lstrcmp(res, szSuccess))
MessageBox(0, res, NULL, MB_ICONERROR);
}
void internal_unzip(int uselog)
{
//All char baby (or should I use int for the non-string stuff??)
TCHAR first=0;
TCHAR filefound=0;
TCHAR usefile=0;
TCHAR hastext=0;
TCHAR noextractpath=0;
TCHAR filetoextract[MAX_PATH+1];
TCHAR buf[1024];
TCHAR fn[MAX_PATH+1];
popstring(buf);
while (buf[0] == _T('/'))
{
if (!lstrcmpi(buf+1, _T("text"))) popstring(g_extract_text), hastext++;
if (!lstrcmpi(buf+1, _T("noextractpath"))) noextractpath++;
if (!lstrcmpi(buf+1, _T("file")))
{
TCHAR *p;
popstring(filetoextract);
// Ensure filename uses backslashes
p = filetoextract;
while (*p)
{
if (*p == _T('/')) *p=_T('\\');
p++;
}
usefile++;
}
//if stack is empty, bail out
if (popstring(buf))
*buf = 0;
}
//check for first required param
if (*buf)
lstrcpyn(fn, buf, MAX_PATH);
else
{
pushstring(_T("Error reading ZIP filename parameter"));
return;
}
if (popstring(tempzip_path))
{
pushstring(_T("Error reading destination directory parameter"));
return;
}
if (uselog==1)
{
if (!hastext) lstrcpy(g_extract_text, szDefExtractText);
}
unzFile f;
f = unzOpen(_T2A(fn));
if (!f || unzGoToFirstFile(f) != UNZ_OK)
{
if (f) unzClose(f);
pushstring(_T("Error opening ZIP file"));
return;
}
int nf=0, nb=0;
unz_file_info fileinfo;
g_extracting=1;
do {
char filenameA[MAX_PATH];
TCHAR filename[MAX_PATH];
unzGetCurrentFileInfo(f,&fileinfo,filenameA,sizeof(filename),NULL,0,NULL,0);
lstrcpyn(filename, _A2T(filenameA), MAX_PATH);
if (filename[0] &&
filename[_tcsclen(filename)-1] != _T('\\') &&
filename[_tcsclen(filename)-1] != _T('/'))
{
TCHAR *pfn=filename;
//ensure path uses backslashes
while (*pfn)
{
if (*pfn == _T('/')) *pfn=_T('\\');
pfn++;
}
if (usefile)
{
if (lstrcmpi(filename, filetoextract) != 0) continue;
else filefound++;
}
pfn=filename;
if (pfn[1] == _T(':') && pfn[2] == _T('\\')) pfn+=3;
while (*pfn == _T('\\')) pfn++;
if (noextractpath)
{
TCHAR buf[MAX_PATH];
lstrcpy(buf,filename);
TCHAR *p=buf+_tcsclen(buf);
while (p > buf && *p != _T('\\') && *p != _T('/')) p = CharPrev(buf, p);
if (p > buf) p++;
lstrcpy(filename, p);
}
TCHAR out_filename[1024];
lstrcpy(out_filename,tempzip_path);
lstrcat(out_filename,_T("\\"));
lstrcat(out_filename,pfn);
if (_tcsstr(pfn,_T("\\")))
{
TCHAR buf[1024];
lstrcpy(buf,out_filename);
TCHAR *p=buf+_tcsclen(buf);
while (p > buf && *p != _T('\\')) p--;
*p=0;
if (buf[0]) doMKDir(buf);
}
if (unzOpenCurrentFile(f) == UNZ_OK)
{
FILE *fp;
int l;
fp = _tfopen(out_filename,_T("wb"));
if (fp)
{
if (uselog==1) {
TCHAR logtmp[256];
parse(g_extract_text, logtmp, sizeof(logtmp),
pfn, fileinfo.compressed_size, fileinfo.uncompressed_size, 0);
LogMessage(g_hwndParent, logtmp);
} else if (uselog == 2) {
if (!first)
{
pushstring(_T("")); //push list terminator (empty string)
first++;
}
pushstring(pfn);
}
nb=0;
do
{
TCHAR buf[1024];
l=unzReadCurrentFile(f,buf,sizeof(buf));
if (l > 0)
{
if (fwrite(buf,1,l,fp) != (unsigned int)l)
{
unzClose(f);
fclose(fp);
pushstring(_T("Error writing output file(s)"));
g_extracting=0;
return;
}
if (!g_extracting)
{
unzClose(f);
fclose(fp);
g_extracting=0;
pushstring(_T("aborted"));
return;
}
}
if (uselog==1 && (nb % 1024))
{
TCHAR logtmp[256];
parse(g_extract_text, logtmp, sizeof(logtmp),
pfn, fileinfo.compressed_size,
fileinfo.uncompressed_size, nb);
LogMessage(g_hwndParent, logtmp, 1);
}
nb += l;
} while (l > 0);
fclose(fp);
}
else
{
unzClose(f);
pushstring(_T("Error opening output file(s)"));
g_extracting=0;
return;
}
nf++;
int quit=0;
if (g_hwndParent)
{
MSG msg;
while (PeekMessage(&msg,NULL,0,0,PM_REMOVE))
{
if (msg.message == WM_DESTROY && msg.hwnd == g_hwndParent)
{
quit++;
break;
}
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
unzCloseCurrentFile(f);
if (quit) break;
}
else
{
unzClose(f);
pushstring(_T("Error extracting from ZIP file"));
g_extracting=0;
return;
}
}
} while (unzGoToNextFile(f) == UNZ_OK);
g_extracting=0;
if (usefile && !filefound) {
pushstring(_T("File not found in archive"));
} else {
pushstring(szSuccess);
}
unzClose(f);
return;
}
// Tim Kosse's LogMessage
void LogMessage(HWND hwndParent, const TCHAR *pStr, int changelast) {
static HWND hwndList=0;
static HWND hwndStatus=0;
LVITEM item={0};
int nItemCount;
if (!hwndParent) return;
EnterCriticalSection(&cs);
//Get ListView control on instfiles page (the log)
if (!hwndList) hwndList = FindWindowEx(
FindWindowEx(hwndParent, NULL, _T("#32770"), NULL),
NULL, _T("SysListView32"), NULL);
//Get status "STATIC" control above progressbar
if (!hwndStatus) hwndStatus = GetDlgItem(
FindWindowEx(hwndParent, NULL, _T("#32770"), NULL),
1006);
if (!hwndList || !hwndStatus) return;
SendMessage(hwndStatus, WM_SETTEXT, 0, (LPARAM)pStr);//added by Saivert
nItemCount=SendMessage(hwndList, LVM_GETITEMCOUNT, 0, 0);
item.mask=LVIF_TEXT;
item.pszText=(TCHAR *)pStr;
item.cchTextMax=0;
item.iItem=changelast?nItemCount-1:nItemCount;
if (changelast) {
ListView_SetItem(hwndList, &item);
} else {
ListView_InsertItem(hwndList, &item);
}
ListView_EnsureVisible(hwndList, item.iItem, 0);
LeaveCriticalSection(&cs);
}
// EXPERIMENTAL THREAD BASED EXTRACTION
/*
extern "C" __declspec(dllexport)
UnzipToLogUsingThread(HWND hwndParent, int string_size, char *variables, stack_t **stacktop)
{
MSG msg;
DWORD dwTID;
CreateThread(NULL, 0, UnzipThread, (LPVOID)1, 0, &dwTID);
}
DWORD WINAPI UnzipThread(LPVOID p)
{
int uselog = 1;
return 0;
}
*/
#ifdef UNICODE
unsigned char staticCnvBuffer[1024*sizeof(TCHAR)]; /* temp buffer, holds ASCII & UNICODE string after conversion */
char * _T2A(wchar_t *wideStr)
{
WideCharToMultiByte(CP_ACP, 0, wideStr, -1, (LPSTR)staticCnvBuffer, sizeof(staticCnvBuffer), NULL, NULL);
return (char *)staticCnvBuffer;
}
wchar_t * _A2T(char *ansiStr)
{
MultiByteToWideChar(CP_ACP, 0, ansiStr, -1, (TCHAR *)staticCnvBuffer, sizeof(staticCnvBuffer)/sizeof(TCHAR));
return (wchar_t *)staticCnvBuffer;
}
#endif