aboutsummaryrefslogtreecommitdiff
path: root/Src/Plugins/General/gen_crasher
diff options
context:
space:
mode:
Diffstat (limited to 'Src/Plugins/General/gen_crasher')
-rw-r--r--Src/Plugins/General/gen_crasher/ExceptionHandler.cpp814
-rw-r--r--Src/Plugins/General/gen_crasher/ExceptionHandler.h33
-rw-r--r--Src/Plugins/General/gen_crasher/GetWinVer.cpp195
-rw-r--r--Src/Plugins/General/gen_crasher/GetWinVer.h76
-rw-r--r--Src/Plugins/General/gen_crasher/MiniVersion.cpp267
-rw-r--r--Src/Plugins/General/gen_crasher/MiniVersion.h69
-rw-r--r--Src/Plugins/General/gen_crasher/ReadMe.txt3
-rw-r--r--Src/Plugins/General/gen_crasher/api__gen_crasher.h20
-rw-r--r--Src/Plugins/General/gen_crasher/config.cpp308
-rw-r--r--Src/Plugins/General/gen_crasher/config.h62
-rw-r--r--Src/Plugins/General/gen_crasher/configDlg.cpp457
-rw-r--r--Src/Plugins/General/gen_crasher/configDlg.h15
-rw-r--r--Src/Plugins/General/gen_crasher/crashDlg.cpp101
-rw-r--r--Src/Plugins/General/gen_crasher/crashDlg.h5
-rw-r--r--Src/Plugins/General/gen_crasher/feedback/email/SendEmail.cpp364
-rw-r--r--Src/Plugins/General/gen_crasher/feedback/email/SendEmail.h38
-rw-r--r--Src/Plugins/General/gen_crasher/feedback/feedback.rc119
-rw-r--r--Src/Plugins/General/gen_crasher/feedback/feedback.sln30
-rw-r--r--Src/Plugins/General/gen_crasher/feedback/feedback.vcproj321
-rw-r--r--Src/Plugins/General/gen_crasher/feedback/feedback.vcxproj253
-rw-r--r--Src/Plugins/General/gen_crasher/feedback/feedback.vcxproj.filters79
-rw-r--r--Src/Plugins/General/gen_crasher/feedback/main.cpp79
-rw-r--r--Src/Plugins/General/gen_crasher/feedback/main.h19
-rw-r--r--Src/Plugins/General/gen_crasher/feedback/manifest.xml36
-rw-r--r--Src/Plugins/General/gen_crasher/feedback/operations.cpp163
-rw-r--r--Src/Plugins/General/gen_crasher/feedback/resource.h29
-rw-r--r--Src/Plugins/General/gen_crasher/feedback/silent.cpp134
-rw-r--r--Src/Plugins/General/gen_crasher/feedback/smtp/Base64.cpp320
-rw-r--r--Src/Plugins/General/gen_crasher/feedback/smtp/Base64.h63
-rw-r--r--Src/Plugins/General/gen_crasher/feedback/smtp/Smtp.cpp1468
-rw-r--r--Src/Plugins/General/gen_crasher/feedback/smtp/Smtp.h245
-rw-r--r--Src/Plugins/General/gen_crasher/feedback/version.rc239
-rw-r--r--Src/Plugins/General/gen_crasher/feedback/xzip/XZip.cpp2915
-rw-r--r--Src/Plugins/General/gen_crasher/feedback/xzip/XZip.h324
-rw-r--r--Src/Plugins/General/gen_crasher/gen_crasher.rc193
-rw-r--r--Src/Plugins/General/gen_crasher/gen_crasher.sln31
-rw-r--r--Src/Plugins/General/gen_crasher/gen_crasher.vcxproj290
-rw-r--r--Src/Plugins/General/gen_crasher/gen_crasher.vcxproj.filters92
-rw-r--r--Src/Plugins/General/gen_crasher/main.cpp140
-rw-r--r--Src/Plugins/General/gen_crasher/main.h24
-rw-r--r--Src/Plugins/General/gen_crasher/minidump.h29
-rw-r--r--Src/Plugins/General/gen_crasher/resource.h96
-rw-r--r--Src/Plugins/General/gen_crasher/settings.cpp251
-rw-r--r--Src/Plugins/General/gen_crasher/settings.h67
-rw-r--r--Src/Plugins/General/gen_crasher/smtpDlg.cpp127
-rw-r--r--Src/Plugins/General/gen_crasher/smtpDlg.h5
-rw-r--r--Src/Plugins/General/gen_crasher/version.rc239
47 files changed, 10847 insertions, 0 deletions
diff --git a/Src/Plugins/General/gen_crasher/ExceptionHandler.cpp b/Src/Plugins/General/gen_crasher/ExceptionHandler.cpp
new file mode 100644
index 00000000..ae1c2399
--- /dev/null
+++ b/Src/Plugins/General/gen_crasher/ExceptionHandler.cpp
@@ -0,0 +1,814 @@
+// ExceptionHandler.cpp Version 1.4
+//
+// Copyright © 1998 Bruce Dawson
+//
+// This source file contains the exception handler for recording error
+// information after crashes. See ExceptionHandler.h for information
+// on how to hook it in.
+//
+// Author: Bruce Dawson
+// brucedawson@cygnus-software.com
+//
+// Modified by: Hans Dietrich
+// hdietrich2@hotmail.com
+//
+// Version 1.4: - Added invocation of XCrashReport.exe
+//
+// Version 1.3: - Added minidump output
+//
+// Version 1.1: - reformatted output for XP-like error report
+// - added ascii output to stack dump
+//
+// A paper by the original author can be found at:
+// http://www.cygnus-software.com/papers/release_debugging.html
+//
+///////////////////////////////////////////////////////////////////////////////
+
+// Disable warnings generated by the Windows header files.
+#pragma warning(disable : 4514)
+#pragma warning(disable : 4201)
+
+#define _WIN32_WINDOWS 0x0500 // for IsDebuggerPresent
+
+#include "windows.h"
+#include <tchar.h>
+#include "GetWinVer.h"
+#include "miniversion.h"
+#include "../nu/ns_wc.h"
+
+#include "minidump.h"
+#include ".\settings.h"
+#include "api__gen_crasher.h"
+
+extern char *winampVersion;
+extern Settings settings;
+
+#ifndef _countof
+#define _countof(array) (sizeof(array)/sizeof(array[0]))
+#endif
+
+const int NumCodeBytes = 16; // Number of code bytes to record.
+const int MaxStackDump = 3072; // Maximum number of DWORDS in stack dumps.
+const int StackColumns = 4; // Number of columns in stack dump.
+
+#define ONEK 1024
+#define SIXTYFOURK (64*ONEK)
+#define ONEM (ONEK*ONEK)
+#define ONEG (ONEK*ONEK*ONEK)
+
+
+///////////////////////////////////////////////////////////////////////////////
+// lstrrchr (avoid the C Runtime )
+static TCHAR * lstrrchr(LPCTSTR string, int ch)
+{
+ TCHAR *start = (TCHAR *)string;
+
+ while (string && *string++) /* find end of string */
+ ;
+ /* search towards front */
+ while (--string != start && *string != (TCHAR) ch)
+ ;
+
+ if (*string == (TCHAR) ch) /* char found ? */
+ return (TCHAR *)string;
+
+ return NULL;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// hprintf behaves similarly to printf, with a few vital differences.
+// It uses wvsprintf to do the formatting, which is a system routine,
+// thus avoiding C run time interactions. For similar reasons it
+// uses WriteFile rather than fwrite.
+// The one limitation that this imposes is that wvsprintf, and
+// therefore hprintf, cannot handle floating point numbers.
+
+// Too many calls to WriteFile can take a long time, causing
+// confusing delays when programs crash. Therefore I implemented
+// a simple buffering scheme for hprintf
+
+#define HPRINTF_BUFFER_SIZE (8*1024) // must be at least 2048
+static wchar_t hprintf_buffer[HPRINTF_BUFFER_SIZE]; // wvsprintf never prints more than one K.
+static int hprintf_index = 0;
+
+///////////////////////////////////////////////////////////////////////////////
+// hflush
+static void hflush(HANDLE LogFile)
+{
+ if (hprintf_index > 0)
+ {
+ DWORD NumBytes = 0;
+ WriteFile(LogFile, hprintf_buffer, lstrlenW(hprintf_buffer)*2, &NumBytes, 0);
+ hprintf_index = 0;
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// hprintf
+static void hprintf(HANDLE LogFile, const wchar_t *Format, ...)
+{
+ if (hprintf_index > (HPRINTF_BUFFER_SIZE-1024))
+ {
+ DWORD NumBytes = 0;
+ WriteFile(LogFile, hprintf_buffer, lstrlen(hprintf_buffer)*2, &NumBytes, 0);
+ hprintf_index = 0;
+ }
+
+ va_list arglist;
+ va_start( arglist, Format);
+ hprintf_index += vswprintf(&hprintf_buffer[hprintf_index], Format, arglist);
+ va_end( arglist);
+}
+
+#include <strsafe.h>
+
+///////////////////////////////////////////////////////////////////////////////
+// DumpMiniDump
+static BOOL DumpMiniDump(HANDLE hFile, PEXCEPTION_POINTERS excpInfo)
+{
+ if (excpInfo == NULL)
+ {
+ // Generate exception to get proper context in dump
+ __try
+ {
+ //OutputDebugString(_T("raising exception\r\n"));
+ RaiseException(EXCEPTION_BREAKPOINT, 0, 0, NULL);
+ }
+ __except(DumpMiniDump(hFile, GetExceptionInformation()), EXCEPTION_CONTINUE_EXECUTION)
+ {
+ }
+ }
+ else
+ {
+ //OutputDebugString(_T("writing minidump\r\n"));
+ MINIDUMP_EXCEPTION_INFORMATION eInfo = {0};
+ eInfo.ThreadId = GetCurrentThreadId();
+ eInfo.ExceptionPointers = excpInfo;
+ eInfo.ClientPointers = FALSE;
+
+ // try to load dbghelpdll
+ HMODULE hm = NULL;
+ // first from app folder
+ wchar_t szDbgHelpPath[_MAX_PATH] = {0};
+
+ if (GetModuleFileNameW( NULL, szDbgHelpPath, _MAX_PATH ))
+ {
+ wchar_t *pSlash = wcsrchr( szDbgHelpPath, L'\\' );
+ if (pSlash)
+ {
+ StringCchCopy( pSlash+1, _MAX_PATH, L"dbghelp.dll");
+ hm = LoadLibraryW( szDbgHelpPath );
+ }
+ }
+ if (!hm)
+ {
+ // load any version we can
+ hm = LoadLibraryW(L"dbghelp.dll");
+ }
+
+ if (hm)
+ {
+ BOOL (WINAPI* MiniDumpWriteDump)(
+ HANDLE hProcess,
+ DWORD ProcessId,
+ HANDLE hFile,
+ MINIDUMP_TYPE DumpType,
+ PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam,
+ PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam,
+ PMINIDUMP_CALLBACK_INFORMATION CallbackParam
+ ) = NULL;
+ //OutputDebugString(_T("Found dbghelp.dll, searching for MiniDumpWriteDump\r\n"));
+ *(FARPROC*)&MiniDumpWriteDump = GetProcAddress(hm, "MiniDumpWriteDump");
+ if (MiniDumpWriteDump)
+ {
+ //OutputDebugString(_T("Calling MiniDumpWriteDump\r\n"));
+ BOOL ret = MiniDumpWriteDump(
+ GetCurrentProcess(),
+ GetCurrentProcessId(),
+ hFile,
+ (MINIDUMP_TYPE)settings.dumpType,
+ excpInfo ? &eInfo : NULL,
+ NULL,
+ NULL);
+ //OutputDebugString(_T("MiniDumpWriteDump finished\r\n"));
+ if (!ret)
+ {
+ DWORD le = GetLastError();
+ wchar_t tmp[256] = {0};
+ StringCchPrintfW(tmp, 256, L"call failed with error code: %d", le);
+ //OutputDebugString(tmp);
+ }
+ return ret;
+ }
+ }
+ }
+ return FALSE;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// FormatTime
+//
+// Format the specified FILETIME to output in a human readable format,
+// without using the C run time.
+static void FormatTime(LPTSTR output, FILETIME TimeToPrint)
+{
+ output[0] = _T('\0');
+ WORD Date, Time;
+ if (FileTimeToLocalFileTime(&TimeToPrint, &TimeToPrint) &&
+ FileTimeToDosDateTime(&TimeToPrint, &Date, &Time))
+ {
+ StringCchPrintf(output, 100, _T("%d/%d/%d %02d:%02d:%02d"),
+ (Date / 32) & 15, Date & 31, (Date / 512) + 1980,
+ (Time >> 11), (Time >> 5) & 0x3F, (Time & 0x1F) * 2);
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// DumpModuleInfo
+//
+// Print information about a code module (DLL or EXE) such as its size,
+// location, time stamp, etc.
+static bool DumpModuleInfo(HANDLE LogFile, HINSTANCE ModuleHandle, int nModuleNo)
+{
+ bool rc = false;
+ wchar_t szModName[MAX_PATH*2] = {0};
+ __try
+ {
+ if (GetModuleFileName(ModuleHandle, szModName, MAX_PATH*2) > 0)
+ {
+ // If GetModuleFileName returns greater than zero then this must
+ // be a valid code module address. Therefore we can try to walk
+ // our way through its structures to find the link time stamp.
+ IMAGE_DOS_HEADER *DosHeader = (IMAGE_DOS_HEADER*)ModuleHandle;
+ if (IMAGE_DOS_SIGNATURE != DosHeader->e_magic)
+ return false;
+
+ IMAGE_NT_HEADERS *NTHeader = (IMAGE_NT_HEADERS*)((char *)DosHeader
+ + DosHeader->e_lfanew);
+ if (IMAGE_NT_SIGNATURE != NTHeader->Signature)
+ return false;
+
+ // open the code module file so that we can get its file date and size
+ HANDLE ModuleFile = CreateFile(szModName, GENERIC_READ,
+ FILE_SHARE_READ, 0, OPEN_EXISTING,
+ FILE_ATTRIBUTE_NORMAL, 0);
+
+ TCHAR TimeBuffer[100] = {0};
+ DWORD FileSize = 0;
+ if (ModuleFile != INVALID_HANDLE_VALUE)
+ {
+ FileSize = GetFileSize(ModuleFile, 0);
+ FILETIME LastWriteTime;
+ if (GetFileTime(ModuleFile, 0, 0, &LastWriteTime))
+ {
+ FormatTime(TimeBuffer, LastWriteTime);
+ }
+ CloseHandle(ModuleFile);
+ }
+ hprintf(LogFile, _T("Module %d\r\n"), nModuleNo);
+ hprintf(LogFile, _T("%s\r\n"), szModName);
+ hprintf(LogFile, _T("Image Base: 0x%08x Image Size: 0x%08x\r\n"),
+ NTHeader->OptionalHeader.ImageBase,
+ NTHeader->OptionalHeader.SizeOfImage),
+
+ hprintf(LogFile, _T("Checksum: 0x%08x Time Stamp: 0x%08x\r\n"),
+ NTHeader->OptionalHeader.CheckSum,
+ NTHeader->FileHeader.TimeDateStamp);
+
+ hprintf(LogFile, _T("File Size: %-10d File Time: %s\r\n"),
+ FileSize, TimeBuffer);
+
+ hprintf(LogFile, _T("Version Information:\r\n"));
+
+ CMiniVersion ver(szModName);
+ TCHAR szBuf[200] = {0};
+ WORD dwBuf[4] = {0};
+
+ ver.GetCompanyName(szBuf, _countof(szBuf)-1);
+ hprintf(LogFile, _T(" Company: %s\r\n"), szBuf);
+
+ ver.GetProductName(szBuf, _countof(szBuf)-1);
+ hprintf(LogFile, _T(" Product: %s\r\n"), szBuf);
+
+ ver.GetFileDescription(szBuf, _countof(szBuf)-1);
+ hprintf(LogFile, _T(" FileDesc: %s\r\n"), szBuf);
+
+ ver.GetFileVersion(dwBuf);
+ hprintf(LogFile, _T(" FileVer: %d.%d.%d.%d\r\n"),
+ dwBuf[0], dwBuf[1], dwBuf[2], dwBuf[3]);
+
+ ver.GetProductVersion(dwBuf);
+ hprintf(LogFile, _T(" ProdVer: %d.%d.%d.%d\r\n"),
+ dwBuf[0], dwBuf[1], dwBuf[2], dwBuf[3]);
+
+ ver.Release();
+
+ hprintf(LogFile, _T("\r\n"));
+
+ rc = true;
+ }
+ }
+ // Handle any exceptions by continuing from this point.
+ __except(EXCEPTION_EXECUTE_HANDLER)
+ {
+ //OutputDebugString(L"DumpModuleInfo exception");
+ }
+ return rc;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// DumpModuleList
+//
+// Scan memory looking for code modules (DLLs or EXEs). VirtualQuery is used
+// to find all the blocks of address space that were reserved or committed,
+// and ShowModuleInfo will display module information if they are code
+// modules.
+static void DumpModuleList(HANDLE LogFile)
+{
+ SYSTEM_INFO SystemInfo;
+ GetSystemInfo(&SystemInfo);
+
+ //OutputDebugString(L"Dumping modules list");
+ const size_t PageSize = SystemInfo.dwPageSize;
+
+ // Set NumPages to the number of pages in the 4GByte address space,
+ // while being careful to avoid overflowing ints
+ const size_t NumPages = 4 * size_t(ONEG / PageSize);
+ size_t pageNum = 0;
+ void *LastAllocationBase = 0;
+
+ int nModuleNo = 1;
+
+ while (pageNum < NumPages)
+ {
+ MEMORY_BASIC_INFORMATION MemInfo;
+ if (VirtualQuery((void *)(pageNum * PageSize), &MemInfo, sizeof(MemInfo)))
+ {
+ if (MemInfo.RegionSize > 0)
+ {
+ // Adjust the page number to skip over this block of memory
+ pageNum += MemInfo.RegionSize / PageSize;
+ if (MemInfo.State == MEM_COMMIT && MemInfo.AllocationBase > LastAllocationBase)
+ {
+ // Look for new blocks of committed memory, and try
+ // recording their module names - this will fail
+ // gracefully if they aren't code modules
+ LastAllocationBase = MemInfo.AllocationBase;
+
+ if (DumpModuleInfo(LogFile, (HINSTANCE)LastAllocationBase, nModuleNo))
+ {
+ nModuleNo++;
+ }
+ }
+ }
+ else
+ pageNum += SIXTYFOURK / PageSize;
+ }
+ else
+ pageNum += SIXTYFOURK / PageSize;
+
+ // If VirtualQuery fails we advance by 64K because that is the
+ // granularity of address space doled out by VirtualAlloc()
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// DumpSystemInformation
+//
+// Record information about the user's system, such as processor type, amount
+// of memory, etc.
+static void DumpSystemInformation(HANDLE LogFile)
+{
+ FILETIME CurrentTime;
+ GetSystemTimeAsFileTime(&CurrentTime);
+ TCHAR szTimeBuffer[100] = {0};
+ FormatTime(szTimeBuffer, CurrentTime);
+
+ hprintf(LogFile, _T("Error occurred at %s.\r\n"), szTimeBuffer);
+
+ TCHAR szModuleName[MAX_PATH*2] = {0};
+ if (GetModuleFileName(0, szModuleName, _countof(szModuleName)-2) <= 0)
+ StringCbCopy(szModuleName, sizeof(szModuleName), _T("Unknown"));
+
+ TCHAR szUserName[200] = {0};
+ DWORD UserNameSize = _countof(szUserName)-2;
+ if (!GetUserName(szUserName, &UserNameSize))
+ StringCbCopy(szUserName, sizeof(szUserName), _T("Unknown"));
+
+ hprintf(LogFile, _T("%s, run by %s.\r\n"), szModuleName, szUserName);
+
+ // print out operating system
+ TCHAR szWinVer[50] = {0}, szMajorMinorBuild[50] = {0};
+ int nWinVer = 0;
+ GetWinVer(szWinVer, &nWinVer, szMajorMinorBuild);
+ hprintf(LogFile, _T("Operating system: %s (%s).\r\n"),
+ szWinVer, szMajorMinorBuild);
+
+ SYSTEM_INFO SystemInfo;
+ GetSystemInfo(&SystemInfo);
+ hprintf(LogFile, _T("%d processor(s), type %d.\r\n"),
+ SystemInfo.dwNumberOfProcessors, SystemInfo.dwProcessorType);
+
+ MEMORYSTATUS MemInfo;
+ MemInfo.dwLength = sizeof(MemInfo);
+ GlobalMemoryStatus(&MemInfo);
+
+ // Print out info on memory, rounded up.
+ hprintf(LogFile, _T("%d%% memory in use.\r\n"), MemInfo.dwMemoryLoad);
+ hprintf(LogFile, _T("%d MBytes physical memory.\r\n"), (MemInfo.dwTotalPhys +
+ ONEM - 1) / ONEM);
+ hprintf(LogFile, _T("%d MBytes physical memory free.\r\n"),
+ (MemInfo.dwAvailPhys + ONEM - 1) / ONEM);
+ hprintf(LogFile, _T("%d MBytes paging file.\r\n"), (MemInfo.dwTotalPageFile +
+ ONEM - 1) / ONEM);
+ hprintf(LogFile, _T("%d MBytes paging file free.\r\n"),
+ (MemInfo.dwAvailPageFile + ONEM - 1) / ONEM);
+ hprintf(LogFile, _T("%d MBytes user address space.\r\n"),
+ (MemInfo.dwTotalVirtual + ONEM - 1) / ONEM);
+ hprintf(LogFile, _T("%d MBytes user address space free.\r\n"),
+ (MemInfo.dwAvailVirtual + ONEM - 1) / ONEM);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// GetExceptionDescription
+//
+// Translate the exception code into something human readable
+static const TCHAR *GetExceptionDescription(DWORD ExceptionCode)
+{
+ struct ExceptionNames
+ {
+ DWORD ExceptionCode;
+ TCHAR * ExceptionName;
+ };
+
+#if 0 // from winnt.h
+#define STATUS_WAIT_0 ((DWORD )0x00000000L)
+#define STATUS_ABANDONED_WAIT_0 ((DWORD )0x00000080L)
+#define STATUS_USER_APC ((DWORD )0x000000C0L)
+#define STATUS_TIMEOUT ((DWORD )0x00000102L)
+#define STATUS_PENDING ((DWORD )0x00000103L)
+#define STATUS_SEGMENT_NOTIFICATION ((DWORD )0x40000005L)
+#define STATUS_GUARD_PAGE_VIOLATION ((DWORD )0x80000001L)
+#define STATUS_DATATYPE_MISALIGNMENT ((DWORD )0x80000002L)
+#define STATUS_BREAKPOINT ((DWORD )0x80000003L)
+#define STATUS_SINGLE_STEP ((DWORD )0x80000004L)
+#define STATUS_ACCESS_VIOLATION ((DWORD )0xC0000005L)
+#define STATUS_IN_PAGE_ERROR ((DWORD )0xC0000006L)
+#define STATUS_INVALID_HANDLE ((DWORD )0xC0000008L)
+#define STATUS_NO_MEMORY ((DWORD )0xC0000017L)
+#define STATUS_ILLEGAL_INSTRUCTION ((DWORD )0xC000001DL)
+#define STATUS_NONCONTINUABLE_EXCEPTION ((DWORD )0xC0000025L)
+#define STATUS_INVALID_DISPOSITION ((DWORD )0xC0000026L)
+#define STATUS_ARRAY_BOUNDS_EXCEEDED ((DWORD )0xC000008CL)
+#define STATUS_FLOAT_DENORMAL_OPERAND ((DWORD )0xC000008DL)
+#define STATUS_FLOAT_DIVIDE_BY_ZERO ((DWORD )0xC000008EL)
+#define STATUS_FLOAT_INEXACT_RESULT ((DWORD )0xC000008FL)
+#define STATUS_FLOAT_INVALID_OPERATION ((DWORD )0xC0000090L)
+#define STATUS_FLOAT_OVERFLOW ((DWORD )0xC0000091L)
+#define STATUS_FLOAT_STACK_CHECK ((DWORD )0xC0000092L)
+#define STATUS_FLOAT_UNDERFLOW ((DWORD )0xC0000093L)
+#define STATUS_INTEGER_DIVIDE_BY_ZERO ((DWORD )0xC0000094L)
+#define STATUS_INTEGER_OVERFLOW ((DWORD )0xC0000095L)
+#define STATUS_PRIVILEGED_INSTRUCTION ((DWORD )0xC0000096L)
+#define STATUS_STACK_OVERFLOW ((DWORD )0xC00000FDL)
+#define STATUS_CONTROL_C_EXIT ((DWORD )0xC000013AL)
+#define STATUS_FLOAT_MULTIPLE_FAULTS ((DWORD )0xC00002B4L)
+#define STATUS_FLOAT_MULTIPLE_TRAPS ((DWORD )0xC00002B5L)
+#define STATUS_ILLEGAL_VLM_REFERENCE ((DWORD )0xC00002C0L)
+#endif
+
+ ExceptionNames ExceptionMap[] =
+ {
+ {0x40010005, _T("a Control-C")},
+ {0x40010008, _T("a Control-Break")},
+ {0x80000002, _T("a Datatype Misalignment")},
+ {0x80000003, _T("a Breakpoint")},
+ {0xc0000005, _T("an Access Violation")},
+ {0xc0000006, _T("an In Page Error")},
+ {0xc0000017, _T("a No Memory")},
+ {0xc000001d, _T("an Illegal Instruction")},
+ {0xc0000025, _T("a Noncontinuable Exception")},
+ {0xc0000026, _T("an Invalid Disposition")},
+ {0xc000008c, _T("a Array Bounds Exceeded")},
+ {0xc000008d, _T("a Float Denormal Operand")},
+ {0xc000008e, _T("a Float Divide by Zero")},
+ {0xc000008f, _T("a Float Inexact Result")},
+ {0xc0000090, _T("a Float Invalid Operation")},
+ {0xc0000091, _T("a Float Overflow")},
+ {0xc0000092, _T("a Float Stack Check")},
+ {0xc0000093, _T("a Float Underflow")},
+ {0xc0000094, _T("an Integer Divide by Zero")},
+ {0xc0000095, _T("an Integer Overflow")},
+ {0xc0000096, _T("a Privileged Instruction")},
+ {0xc00000fD, _T("a Stack Overflow")},
+ {0xc0000142, _T("a DLL Initialization Failed")},
+ {0xe06d7363, _T("a Microsoft C++ Exception")},
+ };
+
+ for (int i = 0; i < _countof(ExceptionMap); i++)
+ if (ExceptionCode == ExceptionMap[i].ExceptionCode)
+ return ExceptionMap[i].ExceptionName;
+
+ return _T("an Unknown exception type");
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// GetFilePart
+static TCHAR * GetFilePart(LPCTSTR source)
+{
+ TCHAR *result = lstrrchr(source, _T('\\'));
+ if (result)
+ result++;
+ else
+ result = (TCHAR *)source;
+ return result;
+}
+
+#ifdef _M_IX86
+///////////////////////////////////////////////////////////////////////////////
+// DumpStack
+static void DumpStack(HANDLE LogFile, DWORD *pStack)
+{
+ hprintf(LogFile, _T("\r\n\r\nStack:\r\n"));
+
+ __try
+ {
+ // Esp contains the bottom of the stack, or at least the bottom of
+ // the currently used area.
+ DWORD* pStackTop;
+
+ __asm
+ {
+ // Load the top (highest address) of the stack from the
+ // thread information block. It will be found there in
+ // Win9x and Windows NT.
+ mov eax, fs:[4]
+ mov pStackTop, eax
+ }
+
+ if (pStackTop > pStack + MaxStackDump)
+ pStackTop = pStack + MaxStackDump;
+
+ int Count = 0;
+
+ DWORD* pStackStart = pStack;
+
+ int nDwordsPrinted = 0;
+
+ while (pStack + 1 <= pStackTop)
+ {
+ if ((Count % StackColumns) == 0)
+ {
+ pStackStart = pStack;
+ nDwordsPrinted = 0;
+ hprintf(LogFile, _T("0x%08x: "), pStack);
+ }
+ hprintf(LogFile, _T("%08x "), pStack);
+ if ((++Count % StackColumns) == 0 || pStack + 2 > pStackTop)
+ {
+ nDwordsPrinted++;
+ int n = nDwordsPrinted;
+ while (n < 4)
+ {
+ hprintf(LogFile, _T(" "));
+ n++;
+ }
+
+ for (int i = 0; i < nDwordsPrinted; i++)
+ {
+ DWORD dwStack = *pStackStart;
+ for (int j = 0; j < 4; j++)
+ {
+ char c = (char)(dwStack & 0xFF);
+ if (c < 0x20 || c > 0x7E)
+ c = '.';
+#ifdef _UNICODE
+ WCHAR w = (WCHAR)c;
+ hprintf(LogFile, _T("%c"), w);
+#else
+ hprintf(LogFile, _T("%c"), c);
+#endif
+ dwStack = dwStack >> 8;
+ }
+ pStackStart++;
+ }
+
+ hprintf(LogFile, _T("\r\n"));
+ }
+ else
+ {
+ // hprintf(LogFile, _T("%08x "), *pStack);
+ nDwordsPrinted++;
+ }
+ pStack++;
+ }
+ hprintf(LogFile, _T("\r\n"));
+ }
+ __except(EXCEPTION_EXECUTE_HANDLER)
+ {
+ hprintf(LogFile, _T("Exception encountered during stack dump.\r\n"));
+ }
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+// DumpRegisters
+static void DumpRegisters(HANDLE LogFile, PCONTEXT Context)
+{
+ // Print out the register values in an XP error window compatible format.
+ hprintf(LogFile, _T("\r\n"));
+ hprintf(LogFile, _T("Context:\r\n"));
+ hprintf(LogFile, _T("EDI: 0x%08x ESI: 0x%08x EAX: 0x%08x\r\n"),
+ Context->Edi, Context->Esi, Context->Eax);
+ hprintf(LogFile, _T("EBX: 0x%08x ECX: 0x%08x EDX: 0x%08x\r\n"),
+ Context->Ebx, Context->Ecx, Context->Edx);
+ hprintf(LogFile, _T("EIP: 0x%08x EBP: 0x%08x SegCs: 0x%08x\r\n"),
+ Context->Eip, Context->Ebp, Context->SegCs);
+ hprintf(LogFile, _T("EFlags: 0x%08x ESP: 0x%08x SegSs: 0x%08x\r\n"),
+ Context->EFlags, Context->Esp, Context->SegSs);
+}
+
+#endif
+
+BOOL CreateLog(PEXCEPTION_POINTERS pExceptPtrs, LPCWSTR lpszMessage)
+{
+ HANDLE hLogFile = CreateFile(settings.logPath, GENERIC_WRITE, 0, 0,
+ CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_WRITE_THROUGH, 0);
+
+ if (hLogFile == INVALID_HANDLE_VALUE)
+ {
+ //OutputDebugString(_T("Error creating exception report\r\n"));
+ return FALSE;
+ }
+
+ // add BOM
+ WORD wBOM = 0xFEFF;
+ DWORD num = 0;
+ WriteFile(hLogFile, &wBOM, sizeof(WORD), &num, NULL);
+ // Append to the error log
+ SetFilePointer(hLogFile, 0, 0, FILE_END);
+
+ wchar_t line[1024] = {0};
+ wchar_t msgBody[4*1024] = {0};
+ wchar_t winampVersionWide[1024] = {0};
+ MultiByteToWideCharSZ(CP_ACP, 0, winampVersion, -1, winampVersionWide, 1024);
+ StringCchPrintf(line, 1024, L"Winamp client version: %s\r\n", winampVersionWide);
+ StringCchCopy(msgBody, 4*1024, line);
+
+ PEXCEPTION_RECORD Exception = pExceptPtrs->ExceptionRecord;
+ PCONTEXT Context = pExceptPtrs->ContextRecord;
+
+ TCHAR szCrashModulePathName[MAX_PATH*2] = {0};
+
+ TCHAR *pszCrashModuleFileName = _T("Unknown");
+
+ #ifdef _M_IX86
+ MEMORY_BASIC_INFORMATION MemInfo;
+
+ // VirtualQuery can be used to get the allocation base associated with a
+ // code address, which is the same as the ModuleHandle. This can be used
+ // to get the filename of the module that the crash happened in.
+
+ if (VirtualQuery((void*)Context->Eip, &MemInfo, sizeof(MemInfo)) &&
+ (GetModuleFileName((HINSTANCE)MemInfo.AllocationBase,
+ szCrashModulePathName,
+ sizeof(szCrashModulePathName)-2) > 0))
+ {
+ //OutputDebugString(szCrashModulePathName);
+ pszCrashModuleFileName = GetFilePart(szCrashModulePathName);
+ }
+ #endif
+
+ // Print out the beginning of the error log in a Win95 error window
+ // compatible format.
+ TCHAR szModuleName[MAX_PATH*2] = {0};
+ if (GetModuleFileName(0, szModuleName, _countof(szModuleName)-2) <= 0)
+ StringCbCopy(szModuleName, sizeof(szModuleName), _T("Unknown"));
+
+ TCHAR *pszFilePart = GetFilePart(szModuleName);
+
+ // Extract the file name portion and remove it's file extension
+ TCHAR szFileName[MAX_PATH*2] = {0};
+ StringCbCopy(szFileName, sizeof(szFileName), pszFilePart);
+ TCHAR *lastperiod = lstrrchr(szFileName, _T('.'));
+ if (lastperiod)
+ lastperiod[0] = 0;
+
+ #ifdef _M_IX86
+ StringCchPrintf(line, 1024, L"%s caused %s (0x%08x) \r\nin module %s at %04x:%08x.\r\n\r\n",
+ szFileName, GetExceptionDescription(Exception->ExceptionCode),
+ Exception->ExceptionCode,
+ pszCrashModuleFileName, Context->SegCs, Context->Eip);
+ #endif
+ StringCchCat(msgBody, 4*1024, line);
+
+ StringCchPrintf(line, 1024, L"Exception handler called in %s.\r\n", lpszMessage);
+ StringCchCat(msgBody, 4*1024, line);
+
+ hprintf(hLogFile, L"%s", msgBody);
+ wchar_t *p = msgBody, *end = msgBody + wcslen(msgBody);
+ while(p != end)
+ {
+ if (*p == L'\r') *p = 1;
+ if (*p == L'\n') *p = 2;
+ p++;
+
+ }
+ settings.WriteBody(msgBody);
+
+ if (settings.logSystem)
+ {
+ DumpSystemInformation(hLogFile);
+
+ // If the exception was an access violation, print out some additional
+ // information, to the error log and the debugger.
+ if (Exception->ExceptionCode == STATUS_ACCESS_VIOLATION &&
+ Exception->NumberParameters >= 2)
+ {
+ TCHAR szDebugMessage[1000] = {0};
+ const TCHAR* readwrite = _T("Read from");
+ if (Exception->ExceptionInformation[0])
+ readwrite = _T("Write to");
+ StringCchPrintf(szDebugMessage, 1000, _T("%s location %08x caused an access violation.\r\n"),
+ readwrite, Exception->ExceptionInformation[1]);
+ hprintf(hLogFile, _T("%s"), szDebugMessage);
+ }
+ }
+ if (settings.logRegistry)
+ {
+ #ifdef _M_IX86
+ DumpRegisters(hLogFile, Context);
+ #endif
+
+ // Print out the bytes of code at the instruction pointer. Since the
+ // crash may have been caused by an instruction pointer that was bad,
+ // this code needs to be wrapped in an exception handler, in case there
+ // is no memory to read. If the dereferencing of code[] fails, the
+ // exception handler will print '??'.
+ #ifdef _M_IX86
+ hprintf(hLogFile, _T("\r\nBytes at CS:EIP:\r\n"));
+ BYTE * code = (BYTE *)Context->Eip;
+ for (int codebyte = 0; codebyte < NumCodeBytes; codebyte++)
+ {
+ __try
+ {
+ hprintf(hLogFile, _T("%02x "), code[codebyte]);
+
+ }
+ __except(EXCEPTION_EXECUTE_HANDLER)
+ {
+ hprintf(hLogFile, _T("?? "));
+ }
+ }
+ #endif
+ }
+ if (settings.logStack)
+ {
+ // Time to print part or all of the stack to the error log. This allows
+ // us to figure out the call stack, parameters, local variables, etc.
+
+ // Esp contains the bottom of the stack, or at least the bottom of
+ // the currently used area
+
+ #ifdef _M_IX86
+ DWORD* pStack = (DWORD *)Context->Esp;
+ DumpStack(hLogFile, pStack);
+ #endif
+ }
+ if (settings.logModule)
+ {
+ DumpModuleList(hLogFile);
+ }
+
+ hprintf(hLogFile, _T("\r\n===== [end of log file] =====\r\n"));
+ hflush(hLogFile);
+ CloseHandle(hLogFile);
+ return TRUE;
+}
+
+BOOL CreateDump(PEXCEPTION_POINTERS pExceptPtrs)
+{
+ BOOL retCode = FALSE;
+
+ // Create the file
+ //OutputDebugString(_T("CreateFile: "));
+ //OutputDebugString(settings.dumpPath);
+ HANDLE hMiniDumpFile = CreateFile(
+ settings.dumpPath,
+ GENERIC_WRITE,
+ 0,
+ NULL,
+ CREATE_ALWAYS,
+ FILE_ATTRIBUTE_NORMAL | FILE_FLAG_WRITE_THROUGH,
+ NULL);
+
+ // Write the minidump to the file
+ if (hMiniDumpFile != INVALID_HANDLE_VALUE)
+ {
+ retCode = DumpMiniDump(hMiniDumpFile, pExceptPtrs);
+ // Close file
+ CloseHandle(hMiniDumpFile);
+ if (!retCode) DeleteFile(settings.dumpPath);
+ }
+ return retCode;
+} \ No newline at end of file
diff --git a/Src/Plugins/General/gen_crasher/ExceptionHandler.h b/Src/Plugins/General/gen_crasher/ExceptionHandler.h
new file mode 100644
index 00000000..5b4a2ce2
--- /dev/null
+++ b/Src/Plugins/General/gen_crasher/ExceptionHandler.h
@@ -0,0 +1,33 @@
+// ExceptionHandler.h Version 1.1
+//
+// Copyright © 1998 Bruce Dawson
+//
+// Author: Bruce Dawson
+// brucedawson@cygnus-software.com
+//
+// Modified by: Hans Dietrich
+// hdietrich2@hotmail.com
+//
+// A paper by the original author can be found at:
+// http://www.cygnus-software.com/papers/release_debugging.html
+//
+///////////////////////////////////////////////////////////////////////////////
+
+#ifndef EXCEPTIONHANDLER_H
+#define EXCEPTIONHANDLER_H
+
+BOOL CreateLog(PEXCEPTION_POINTERS pExceptPtrs, LPCWSTR lpszMessage);
+BOOL CreateDump(PEXCEPTION_POINTERS pExceptPtrs);
+
+// We forward declare PEXCEPTION_POINTERS so that the function
+// prototype doesn't needlessly require windows.h.
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif \ No newline at end of file
diff --git a/Src/Plugins/General/gen_crasher/GetWinVer.cpp b/Src/Plugins/General/gen_crasher/GetWinVer.cpp
new file mode 100644
index 00000000..7434d73e
--- /dev/null
+++ b/Src/Plugins/General/gen_crasher/GetWinVer.cpp
@@ -0,0 +1,195 @@
+// GetWinVer.cpp Version 1.1
+//
+// Copyright (C) 2001-2003 Hans Dietrich
+//
+// This software is released into the public domain.
+// You are free to use it in any way you like, except
+// that you may not sell this source code.
+//
+// This software is provided "as is" with no expressed
+// or implied warranty. I accept no liability for any
+// damage or loss of business that this software may cause.
+//
+///////////////////////////////////////////////////////////////////////////////
+
+//#include "tchar.h"
+#include "GetWinVer.h"
+
+// from winbase.h
+#ifndef VER_PLATFORM_WIN32s
+#define VER_PLATFORM_WIN32s 0
+#endif
+#ifndef VER_PLATFORM_WIN32_WINDOWS
+#define VER_PLATFORM_WIN32_WINDOWS 1
+#endif
+#ifndef VER_PLATFORM_WIN32_NT
+#define VER_PLATFORM_WIN32_NT 2
+#endif
+#ifndef VER_PLATFORM_WIN32_CE
+#define VER_PLATFORM_WIN32_CE 3
+#endif
+
+/*
+ This table has been assembled from Usenet postings, personal
+ observations, and reading other people's code. Please feel
+ free to add to it or correct it.
+
+
+ dwPlatFormID dwMajorVersion dwMinorVersion dwBuildNumber
+95 1 4 0 950
+95 SP1 1 4 0 >950 && <=1080
+95 OSR2 1 4 <10 >1080
+98 1 4 10 1998
+98 SP1 1 4 10 >1998 && <2183
+98 SE 1 4 10 >=2183
+ME 1 4 90 3000
+
+NT 3.51 2 3 51
+NT 4 2 4 0 1381
+2000 2 5 0 2195
+XP 2 5 1 2600
+2003 Server 2 5 2 3790
+VISTA 2 6 0 6000
+7 2 6 1 7600
+8 2 6 2 9200
+8.1 2 6 3 9600
+10 2 10 0 10240
+11 2 11 0 22000
+
+CE 3
+
+*/
+
+
+///////////////////////////////////////////////////////////////////////////////
+// GetWinVer
+BOOL GetWinVer(LPWSTR pszVersion, int *nVersion, LPWSTR pszMajorMinorBuild)
+{
+ if (!pszVersion || !nVersion || !pszMajorMinorBuild)
+ return FALSE;
+ lstrcpy(pszVersion, WUNKNOWNSTR);
+ *nVersion = WUNKNOWN;
+
+ DWORD (WINAPI *RtlGetVersion)(LPOSVERSIONINFOEXW);
+ OSVERSIONINFOEXW osinfo;
+ *(FARPROC*)&RtlGetVersion = GetProcAddress(GetModuleHandleW(L"ntdll"), "RtlGetVersion");
+ if (!RtlGetVersion) {
+ return FALSE;
+ }
+ osinfo.dwOSVersionInfoSize = sizeof(osinfo);
+ if (RtlGetVersion(&osinfo)) {
+ return FALSE;
+ }
+
+ DWORD dwPlatformId = osinfo.dwPlatformId;
+ DWORD dwMajorVersion = osinfo.dwMajorVersion;
+ DWORD dwMinorVersion = osinfo.dwMinorVersion;
+ DWORD dwBuildNumber = osinfo.dwBuildNumber & 0xFFFF; // Win 95 needs this
+
+ wsprintfW(pszMajorMinorBuild, L"%u.%u.%u", dwMajorVersion, dwMinorVersion, dwBuildNumber);
+
+ if ((dwPlatformId == VER_PLATFORM_WIN32_WINDOWS) && (dwMajorVersion == 4))
+ {
+ if ((dwMinorVersion < 10) && (dwBuildNumber == 950))
+ {
+ lstrcpy(pszVersion, W95STR);
+ *nVersion = W95;
+ }
+ else if ((dwMinorVersion < 10) &&
+ ((dwBuildNumber > 950) && (dwBuildNumber <= 1080)))
+ {
+ lstrcpy(pszVersion, W95SP1STR);
+ *nVersion = W95SP1;
+ }
+ else if ((dwMinorVersion < 10) && (dwBuildNumber > 1080))
+ {
+ lstrcpy(pszVersion, W95OSR2STR);
+ *nVersion = W95OSR2;
+ }
+ else if ((dwMinorVersion == 10) && (dwBuildNumber == 1998))
+ {
+ lstrcpy(pszVersion, W98STR);
+ *nVersion = W98;
+ }
+ else if ((dwMinorVersion == 10) &&
+ ((dwBuildNumber > 1998) && (dwBuildNumber < 2183)))
+ {
+ lstrcpy(pszVersion, W98SP1STR);
+ *nVersion = W98SP1;
+ }
+ else if ((dwMinorVersion == 10) && (dwBuildNumber >= 2183))
+ {
+ lstrcpy(pszVersion, W98SESTR);
+ *nVersion = W98SE;
+ }
+ else if (dwMinorVersion == 90)
+ {
+ lstrcpy(pszVersion, WMESTR);
+ *nVersion = WME;
+ }
+ }
+ else if (dwPlatformId == VER_PLATFORM_WIN32_NT)
+ {
+ if ((dwMajorVersion == 3) && (dwMinorVersion == 51))
+ {
+ lstrcpy(pszVersion, WNT351STR);
+ *nVersion = WNT351;
+ }
+ else if ((dwMajorVersion == 4) && (dwMinorVersion == 0))
+ {
+ lstrcpy(pszVersion, WNT4STR);
+ *nVersion = WNT4;
+ }
+ else if ((dwMajorVersion == 5) && (dwMinorVersion == 0))
+ {
+ lstrcpy(pszVersion, W2KSTR);
+ *nVersion = W2K;
+ }
+ else if ((dwMajorVersion == 5) && (dwMinorVersion == 1))
+ {
+ lstrcpy(pszVersion, WXPSTR);
+ *nVersion = WXP;
+ }
+ else if ((dwMajorVersion == 5) && (dwMinorVersion == 2))
+ {
+ lstrcpy(pszVersion, W2003SERVERSTR);
+ *nVersion = W2003SERVER;
+ }
+ else if ((dwMajorVersion == 6) && (dwMinorVersion == 0))
+ {
+ lstrcpy(pszVersion, WVSTR);
+ *nVersion = WV;
+ }
+ else if ((dwMajorVersion == 6) && (dwMinorVersion == 1))
+ {
+ lstrcpy(pszVersion, W7STR);
+ *nVersion = W7;
+ }
+ else if ((dwMajorVersion == 6) && (dwMinorVersion == 2))
+ {
+ lstrcpy(pszVersion, W8STR);
+ *nVersion = W8;
+ }
+ else if ((dwMajorVersion == 6) && (dwMinorVersion == 3))
+ {
+ lstrcpy(pszVersion, W81STR);
+ *nVersion = W81;
+ }
+ else if ((dwMajorVersion == 10) && (dwMinorVersion == 0) && (dwBuildNumber < 22000))
+ {
+ lstrcpy(pszVersion, W10STR);
+ *nVersion = W10;
+ }
+ else if ((dwMajorVersion == 10) && (dwMinorVersion == 0) && (dwBuildNumber >= 22000))
+ {
+ lstrcpy(pszVersion, W11STR);
+ *nVersion = W11;
+ }
+ }
+ else if (dwPlatformId == VER_PLATFORM_WIN32_CE)
+ {
+ lstrcpy(pszVersion, WCESTR);
+ *nVersion = WCE;
+ }
+ return TRUE;
+} \ No newline at end of file
diff --git a/Src/Plugins/General/gen_crasher/GetWinVer.h b/Src/Plugins/General/gen_crasher/GetWinVer.h
new file mode 100644
index 00000000..120a7653
--- /dev/null
+++ b/Src/Plugins/General/gen_crasher/GetWinVer.h
@@ -0,0 +1,76 @@
+// GetWinVer.h Version 1.1
+//
+// Copyright (C) 2001-2003 Hans Dietrich
+//
+// This software is released into the public domain.
+// You are free to use it in any way you like, except
+// that you may not sell this source code.
+//
+// This software is provided "as is" with no expressed
+// or implied warranty. I accept no liability for any
+// damage or loss of business that this software may cause.
+//
+///////////////////////////////////////////////////////////////////////////////
+#include <windows.h>
+
+#ifndef GETWINVER_H
+#define GETWINVER_H
+
+#define WUNKNOWNSTR L"Windows [Unknown version]"
+
+#define W95STR L"Windows 95"
+#define W95SP1STR L"Windows 95 SP1"
+#define W95OSR2STR L"Windows 95 OSR2"
+#define W98STR L"Windows 98"
+#define W98SP1STR L"Windows 98 SP1"
+#define W98SESTR L"Windows 98 SE"
+#define WMESTR L"Windows ME"
+
+#define WNT351STR L"Windows NT 3.51"
+#define WNT4STR L"Windows NT 4"
+#define W2KSTR L"Windows 2000"
+#define WXPSTR L"Windows XP"
+#define W2003SERVERSTR L"Windows 2003 Server"
+#define WVSTR L"Windows Vista"
+#define W7STR L"Windows 7"
+#define W8STR L"Windows 8"
+#define W81STR L"Windows 8.1"
+#define W10STR L"Windows 10"
+#define W11STR L"Windows 11"
+
+#define WCESTR L"Windows CE"
+
+
+#define WUNKNOWN 0
+#define W9XFIRST 1
+#define W95 1
+#define W95SP1 2
+#define W95OSR2 3
+#define W98 4
+#define W98SP1 5
+#define W98SE 6
+#define WME 7
+#define W9XLAST 99
+
+#define WNTFIRST 101
+#define WNT351 101
+#define WNT4 102
+#define W2K 103
+#define WXP 104
+#define W2003SERVER 105
+#define WV 106
+#define W7 107
+#define W8 108
+#define W81 109
+#define W10 110
+#define W11 111
+
+#define WNTLAST 199
+
+#define WCEFIRST 201
+#define WCE 201
+#define WCELAST 299
+
+BOOL GetWinVer(LPWSTR pszVersion, int *nVersion, LPWSTR pszMajorMinorBuild);
+
+#endif //GETWINVER_H
diff --git a/Src/Plugins/General/gen_crasher/MiniVersion.cpp b/Src/Plugins/General/gen_crasher/MiniVersion.cpp
new file mode 100644
index 00000000..ece52f26
--- /dev/null
+++ b/Src/Plugins/General/gen_crasher/MiniVersion.cpp
@@ -0,0 +1,267 @@
+// MiniVersion.cpp Version 1.1
+//
+// Author: Hans Dietrich
+// hdietrich2@hotmail.com
+//
+// This software is released into the public domain.
+// You are free to use it in any way you like, except
+// that you may not sell this source code.
+//
+// This software is provided "as is" with no expressed
+// or implied warranty. I accept no liability for any
+// damage or loss of business that this software may cause.
+//
+///////////////////////////////////////////////////////////////////////////////
+
+#include "MiniVersion.h"
+#include <strsafe.h>
+
+///////////////////////////////////////////////////////////////////////////////
+// ctor
+CMiniVersion::CMiniVersion(LPCTSTR lpszPath)
+{
+ ZeroMemory(m_szPath, sizeof(m_szPath));
+
+ if (lpszPath && lpszPath[0] != 0)
+ {
+ lstrcpyn(m_szPath, lpszPath, sizeof(m_szPath)-1);
+ }
+ else
+ {
+ }
+
+ m_pData = NULL;
+ m_dwHandle = 0;
+
+ for (int i = 0; i < 4; i++)
+ {
+ m_wFileVersion[i] = 0;
+ m_wProductVersion[i] = 0;
+ }
+
+ m_dwFileFlags = 0;
+ m_dwFileOS = 0;
+ m_dwFileType = 0;
+ m_dwFileSubtype = 0;
+
+ ZeroMemory(m_szCompanyName, sizeof(m_szCompanyName));
+ ZeroMemory(m_szProductName, sizeof(m_szProductName));
+ ZeroMemory(m_szFileDescription, sizeof(m_szFileDescription));
+
+ Init();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Init
+BOOL CMiniVersion::Init()
+{
+ DWORD dwHandle;
+ DWORD dwSize;
+ BOOL rc;
+
+ dwSize = ::GetFileVersionInfoSize((LPCTSTR)m_szPath, &dwHandle);
+ if (dwSize == 0)
+ return FALSE;
+
+ m_pData = new BYTE [dwSize + 1];
+ ZeroMemory(m_pData, dwSize+1);
+
+ rc = ::GetFileVersionInfo((LPCTSTR)m_szPath, dwHandle, dwSize, m_pData);
+ if (!rc)
+ return FALSE;
+
+ // get fixed info
+
+ VS_FIXEDFILEINFO FixedInfo;
+
+ if (GetFixedInfo(FixedInfo))
+ {
+ m_wFileVersion[0] = HIWORD(FixedInfo.dwFileVersionMS);
+ m_wFileVersion[1] = LOWORD(FixedInfo.dwFileVersionMS);
+ m_wFileVersion[2] = HIWORD(FixedInfo.dwFileVersionLS);
+ m_wFileVersion[3] = LOWORD(FixedInfo.dwFileVersionLS);
+
+ m_wProductVersion[0] = HIWORD(FixedInfo.dwProductVersionMS);
+ m_wProductVersion[1] = LOWORD(FixedInfo.dwProductVersionMS);
+ m_wProductVersion[2] = HIWORD(FixedInfo.dwProductVersionLS);
+ m_wProductVersion[3] = LOWORD(FixedInfo.dwProductVersionLS);
+
+ m_dwFileFlags = FixedInfo.dwFileFlags;
+ m_dwFileOS = FixedInfo.dwFileOS;
+ m_dwFileType = FixedInfo.dwFileType;
+ m_dwFileSubtype = FixedInfo.dwFileSubtype;
+ }
+ else
+ return FALSE;
+
+ // get string info
+
+ GetStringInfo(_T("CompanyName"), m_szCompanyName, MAX_PATH*2);
+ GetStringInfo(_T("FileDescription"), m_szFileDescription, MAX_PATH*2);
+ GetStringInfo(_T("ProductName"), m_szProductName, MAX_PATH*2);
+
+ return TRUE;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Release
+void CMiniVersion::Release()
+{
+ // do this manually, because we can't use objects requiring
+ // a dtor within an exception handler
+ if (m_pData)
+ delete [] m_pData;
+ m_pData = NULL;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// GetFileVersion
+BOOL CMiniVersion::GetFileVersion(WORD * pwVersion)
+{
+ for (int i = 0; i < 4; i++)
+ *pwVersion++ = m_wFileVersion[i];
+ return TRUE;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// GetProductVersion
+BOOL CMiniVersion::GetProductVersion(WORD * pwVersion)
+{
+ for (int i = 0; i < 4; i++)
+ *pwVersion++ = m_wProductVersion[i];
+ return TRUE;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// GetFileFlags
+BOOL CMiniVersion::GetFileFlags(DWORD& rdwFlags)
+{
+ rdwFlags = m_dwFileFlags;
+ return TRUE;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// GetFileOS
+BOOL CMiniVersion::GetFileOS(DWORD& rdwOS)
+{
+ rdwOS = m_dwFileOS;
+ return TRUE;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// GetFileType
+BOOL CMiniVersion::GetFileType(DWORD& rdwType)
+{
+ rdwType = m_dwFileType;
+ return TRUE;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// GetFileSubtype
+BOOL CMiniVersion::GetFileSubtype(DWORD& rdwType)
+{
+ rdwType = m_dwFileSubtype;
+ return TRUE;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// GetCompanyName
+BOOL CMiniVersion::GetCompanyName(LPTSTR lpszCompanyName, int nSize)
+{
+ if (!lpszCompanyName)
+ return FALSE;
+ ZeroMemory(lpszCompanyName, nSize);
+ lstrcpyn(lpszCompanyName, m_szCompanyName, nSize-1);
+ return TRUE;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// GetFileDescription
+BOOL CMiniVersion::GetFileDescription(LPTSTR lpszFileDescription, int nSize)
+{
+ if (!lpszFileDescription)
+ return FALSE;
+ ZeroMemory(lpszFileDescription, nSize);
+ lstrcpyn(lpszFileDescription, m_szFileDescription, nSize-1);
+ return TRUE;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// GetProductName
+BOOL CMiniVersion::GetProductName(LPTSTR lpszProductName, int nSize)
+{
+ if (!lpszProductName)
+ return FALSE;
+ ZeroMemory(lpszProductName, nSize);
+ lstrcpyn(lpszProductName, m_szProductName, nSize-1);
+ return TRUE;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+//
+// protected methods
+//
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+
+
+///////////////////////////////////////////////////////////////////////////////
+// GetFixedInfo
+BOOL CMiniVersion::GetFixedInfo(VS_FIXEDFILEINFO& rFixedInfo)
+{
+ BOOL rc;
+ UINT nLength;
+ VS_FIXEDFILEINFO *pFixedInfo = NULL;
+
+ if (!m_pData)
+ return FALSE;
+
+ if (m_pData)
+ rc = ::VerQueryValue(m_pData, _T("\\"), (void **) &pFixedInfo, &nLength);
+ else
+ rc = FALSE;
+
+ if (rc)
+ memcpy (&rFixedInfo, pFixedInfo, sizeof (VS_FIXEDFILEINFO));
+
+ return rc;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// GetStringInfo
+BOOL CMiniVersion::GetStringInfo(LPCTSTR lpszKey, LPTSTR lpszReturnValue, unsigned int cchBuffer)
+{
+ BOOL rc;
+ DWORD *pdwTranslation;
+ UINT nLength;
+ LPTSTR lpszValue;
+
+ if (m_pData == NULL)
+ return FALSE;
+
+ if (!lpszReturnValue)
+ return FALSE;
+
+ if (!lpszKey)
+ return FALSE;
+
+ *lpszReturnValue = 0;
+
+ rc = ::VerQueryValue(m_pData, _T("\\VarFileInfo\\Translation"),
+ (void**) &pdwTranslation, &nLength);
+ if (!rc)
+ return FALSE;
+
+ TCHAR szKey[2000] = {0};
+ StringCchPrintf(szKey, 2000, _T("\\StringFileInfo\\%04x%04x\\%s"), LOWORD (*pdwTranslation), HIWORD (*pdwTranslation), lpszKey);
+
+ rc = ::VerQueryValue(m_pData, szKey, (void**) &lpszValue, &nLength);
+
+ if (!rc)
+ return FALSE;
+
+ StringCchCopy(lpszReturnValue,cchBuffer, lpszValue);
+
+ return TRUE;
+} \ No newline at end of file
diff --git a/Src/Plugins/General/gen_crasher/MiniVersion.h b/Src/Plugins/General/gen_crasher/MiniVersion.h
new file mode 100644
index 00000000..4a44d97e
--- /dev/null
+++ b/Src/Plugins/General/gen_crasher/MiniVersion.h
@@ -0,0 +1,69 @@
+// MiniVersion.h Version 1.1
+//
+// Author: Hans Dietrich
+// hdietrich2@hotmail.com
+//
+// This software is released into the public domain.
+// You are free to use it in any way you like, except
+// that you may not sell this source code.
+//
+// This software is provided "as is" with no expressed
+// or implied warranty. I accept no liability for any
+// damage or loss of business that this software may cause.
+//
+///////////////////////////////////////////////////////////////////////////////
+
+#ifndef MINIVERSION_H
+#define MINIVERSION_H
+
+#include <windows.h>
+#include <TCHAR.h>
+
+class CMiniVersion
+{
+// constructors
+public:
+ CMiniVersion(LPCTSTR lpszPath = NULL);
+ BOOL Init();
+ void Release();
+
+// operations
+public:
+
+// attributes
+public:
+ // fixed info
+ BOOL GetFileVersion(WORD *pwVersion);
+ BOOL GetProductVersion(WORD* pwVersion);
+ BOOL GetFileFlags(DWORD& rdwFlags);
+ BOOL GetFileOS(DWORD& rdwOS);
+ BOOL GetFileType(DWORD& rdwType);
+ BOOL GetFileSubtype(DWORD& rdwType);
+
+ // string info
+ BOOL GetCompanyName(LPTSTR lpszCompanyName, int nSize);
+ BOOL GetFileDescription(LPTSTR lpszFileDescription, int nSize);
+ BOOL GetProductName(LPTSTR lpszProductName, int nSize);
+
+// implementation
+protected:
+ BOOL GetFixedInfo(VS_FIXEDFILEINFO& rFixedInfo);
+ BOOL GetStringInfo(LPCTSTR lpszKey, LPTSTR lpszValue, unsigned int cchBuffer);
+
+ BYTE* m_pData;
+ DWORD m_dwHandle;
+ WORD m_wFileVersion[4];
+ WORD m_wProductVersion[4];
+ DWORD m_dwFileFlags;
+ DWORD m_dwFileOS;
+ DWORD m_dwFileType;
+ DWORD m_dwFileSubtype;
+
+ TCHAR m_szPath[MAX_PATH*2];
+ TCHAR m_szCompanyName[MAX_PATH*2];
+ TCHAR m_szProductName[MAX_PATH*2];
+ TCHAR m_szFileDescription[MAX_PATH*2];
+};
+
+///////////////////////////////////////////////////////////////////////////////
+#endif \ No newline at end of file
diff --git a/Src/Plugins/General/gen_crasher/ReadMe.txt b/Src/Plugins/General/gen_crasher/ReadMe.txt
new file mode 100644
index 00000000..69bf0a16
--- /dev/null
+++ b/Src/Plugins/General/gen_crasher/ReadMe.txt
@@ -0,0 +1,3 @@
+gen_crasher ver 1.0b
+
+
diff --git a/Src/Plugins/General/gen_crasher/api__gen_crasher.h b/Src/Plugins/General/gen_crasher/api__gen_crasher.h
new file mode 100644
index 00000000..50e0e6fc
--- /dev/null
+++ b/Src/Plugins/General/gen_crasher/api__gen_crasher.h
@@ -0,0 +1,20 @@
+#ifndef NULLSOFT_APIH
+#define NULLSOFT_APIH
+
+#include <api/service/api_service.h>
+extern api_service *serviceManager;
+#define WASABI_API_SVC serviceManager
+
+
+#include <api/application/api_application.h>
+#define WASABI_API_APP applicationApi
+
+
+#include <api/syscb/api_syscb.h>
+#define WASABI_API_SYSCB sysCallbackApi
+
+#include <api/service/waServiceFactory.h>
+
+#include "../Agave/Language/api_language.h"
+
+#endif \ No newline at end of file
diff --git a/Src/Plugins/General/gen_crasher/config.cpp b/Src/Plugins/General/gen_crasher/config.cpp
new file mode 100644
index 00000000..9196bdd1
--- /dev/null
+++ b/Src/Plugins/General/gen_crasher/config.cpp
@@ -0,0 +1,308 @@
+#include <shlwapi.h>
+#include "..\..\..\nu\ns_wc.h"
+#include "config.h"
+#include <strsafe.h>
+
+///////////////////
+///
+/// UNICODE VERSION
+///
+/////
+ConfigW::ConfigW()
+{
+ emptyBOM = FALSE;
+ buff[0] = 0;
+ buffA = NULL;
+ fileName = NULL;
+ defSection = NULL;
+}
+
+ConfigW::ConfigW(const wchar_t *ini, const wchar_t *section)
+{
+ emptyBOM = FALSE;
+ buff[0] = 0;
+ buffA = NULL;
+ fileName = NULL;
+ defSection = NULL;
+ if (SetIniFile(ini)) SetSection(section);
+}
+
+ConfigW::~ConfigW()
+{
+ if (fileName) free(fileName);
+ if (defSection) free(defSection);
+ if (buffA) free(buffA);
+ if (emptyBOM) RemoveEmptyFile();
+}
+
+void ConfigW::Flush(void)
+{
+ return;
+}
+
+BOOL ConfigW::Write(const wchar_t *section, const wchar_t *name, const wchar_t *value)
+{
+ return (fileName) ? WritePrivateProfileString(section, name, value, fileName) : FALSE;
+}
+
+BOOL ConfigW::Write(const wchar_t *section, const wchar_t *name, double value)
+{
+ wchar_t tmp[48] = {0};
+ if (S_OK != StringCchPrintf(tmp, 48, L"%g", value)) return FALSE;
+ return Write(section, name, tmp);
+}
+
+BOOL ConfigW::Write(const wchar_t *section, const wchar_t *name, long long value)
+{
+ wchar_t tmp[32] = {0};
+ if (S_OK != StringCchPrintf(tmp, 32, L"%I64d", value)) return FALSE;
+ return Write(section,name, tmp);
+}
+
+BOOL ConfigW::Write(const wchar_t *section, const wchar_t *name, int value)
+{
+ wchar_t tmp[16] = {0};
+ if (S_OK != StringCchPrintf(tmp, 16, L"%d", value)) return FALSE;
+ return Write(section,name, tmp);
+}
+
+BOOL ConfigW::Write(const wchar_t *section, const wchar_t *name, const char *value)
+{
+ int len = (int)strlen(value) + 1;
+ wchar_t *tmp = (wchar_t*)malloc(len*sizeof(wchar_t));
+ if (!tmp) return FALSE;
+
+ BOOL ret = FALSE;
+ if (MultiByteToWideCharSZ(CP_ACP, 0, value, -1, tmp, len))
+ {
+ ret = Write(section,name, tmp);
+ }
+ free(tmp);
+ return ret;
+}
+
+BOOL ConfigW::Write(const wchar_t *name, int value)
+{
+ if(!defSection) return FALSE;
+ return Write(defSection, name, value);
+}
+
+BOOL ConfigW::Write(const wchar_t *name, long long value)
+{
+ if(!defSection) return FALSE;
+ return Write(defSection, name, value);
+}
+
+BOOL ConfigW::Write(const wchar_t *name, double value)
+{
+ if(!defSection) return FALSE;
+ return Write(defSection, name, value);
+}
+
+BOOL ConfigW::Write(const wchar_t *name, const wchar_t *value)
+{
+ if(!defSection) return FALSE;
+ return Write(defSection, name, value);
+}
+
+BOOL ConfigW::Write(const wchar_t *name, const char value)
+{
+ if(!defSection) return FALSE;
+ return Write(defSection, name, value);
+}
+
+int ConfigW::ReadInt(const wchar_t *section, const wchar_t *name, int defvalue)
+{
+ if (!fileName) return defvalue;
+ return GetPrivateProfileInt(section, name, defvalue, fileName);
+}
+
+long long ConfigW::ReadInt64(const wchar_t *section, const wchar_t *name, long long defvalue)
+{
+ if (!fileName) return defvalue;
+ wchar_t tmp[32] = {0};
+ if (S_OK != StringCchPrintf(tmp, 32, L"%I64d", defvalue)) return defvalue;
+ const wchar_t *ret = ReadStringW(section, name, tmp);
+ return _wtoi64(ret);
+}
+
+double ConfigW::ReadDouble(const wchar_t *section, const wchar_t *name, double defvalue)
+{
+ if (!fileName) return defvalue;
+ wchar_t tmp[32] = {0};
+ if (S_OK != StringCchPrintf(tmp, 32, L"%g", defvalue)) return defvalue;
+ const wchar_t *ret = ReadStringW(section, name, tmp);
+ return _wtof(ret);
+}
+
+const char* ConfigW::ReadStringA(const wchar_t *section, const wchar_t *name, const char *defvalue)
+{
+ if (buffA) free(buffA);
+ if (!fileName) return defvalue;
+
+ int len = (int)strlen(defvalue) + 1;
+ wchar_t *tmp = (wchar_t*)malloc(len *sizeof(wchar_t));
+ if (!tmp) return defvalue;
+
+ if (!MultiByteToWideCharSZ(CP_ACP, 0, defvalue, -1, tmp, len))
+ {
+ free(tmp);
+ return defvalue;
+ }
+ const wchar_t *ret = ReadStringW(section, name, tmp);
+ if (!ret || lstrcmp(ret, tmp) == 0)
+ {
+ free(tmp);
+ return defvalue;
+ }
+ free(tmp);
+
+ len = (int)lstrlen(ret) + 1;
+
+ buffA = (char*)malloc(len*sizeof(char));
+ if (!buffA) return defvalue;
+ if (WideCharToMultiByteSZ(CP_ACP, 0, ret, -1, buffA, len, NULL, NULL))
+ {
+ free(buffA);
+ buffA = NULL;
+ return defvalue;
+ }
+ return buffA;
+}
+
+const wchar_t* ConfigW::ReadStringW(const wchar_t *section, const wchar_t *name, const wchar_t *defvalue)
+{
+ if (!fileName) return defvalue;
+ static wchar_t def[] = L"_$~$_";
+ buff[0] = 0;
+ int len = GetPrivateProfileString(section, name, def, buff, BUFF_SIZE, fileName);
+ if (!len || !lstrcmp(def,buff)) return defvalue;
+ buff[BUFF_SIZE-1]=0;
+ return buff;
+}
+
+int ConfigW::ReadInt(const wchar_t *name, int defvalue)
+{
+ if(!defSection) return defvalue;
+ return ReadInt(defSection, name, defvalue);
+}
+
+long long ConfigW::ReadInt64(const wchar_t *name, long long defvalue)
+{
+ if(!defSection) return defvalue;
+ return ReadInt64(defSection, name, defvalue);
+}
+
+double ConfigW::ReadDouble(const wchar_t *name, double defvalue)
+{
+ if(!defSection) return defvalue;
+ return ReadDouble(defSection, name, defvalue);
+}
+
+const char* ConfigW::ReadStringA(const wchar_t *name, const char *defvalue)
+{
+ if(!defSection) return defvalue;
+ return ReadStringA(defSection, name, defvalue);
+}
+
+const wchar_t* ConfigW::ReadStringW(const wchar_t *name, const wchar_t *defvalue)
+{
+ if(!defSection) return defvalue;
+ return ReadStringW(defSection, name, defvalue);
+}
+
+BOOL ConfigW::SetSection(const wchar_t *section)
+{
+ if (defSection)
+ {
+ free(defSection);
+ defSection = NULL;
+ }
+ size_t len;
+ len = lstrlen(section) + 1;
+ defSection = (wchar_t*)malloc(len*sizeof(wchar_t));
+ if (NULL == defSection)
+ {
+ return FALSE;
+ }
+
+ if (S_OK != StringCchCopy(defSection, len, section))
+ {
+ free(defSection);
+ defSection = NULL;
+ return FALSE;
+ }
+ return TRUE;
+}
+
+BOOL ConfigW::SetIniFile(const wchar_t *file)
+{
+ if (fileName)
+ {
+ if (emptyBOM) RemoveEmptyFile();
+ free(fileName);
+ fileName = NULL;
+ }
+ size_t len;
+ len = lstrlen(file) + 1;
+ fileName = (wchar_t*)malloc(len*sizeof(wchar_t));
+ if (NULL == fileName) return FALSE;
+
+ if (S_OK != StringCchCopy(fileName, len, file))
+ {
+ free(fileName);
+ fileName = NULL;
+ return FALSE;
+ }
+ if (fileName) CreateFileWithBOM();
+ return TRUE;
+}
+
+const wchar_t* ConfigW::GetSection(void)
+{
+ return defSection;
+}
+
+const wchar_t* ConfigW::GetFile(void)
+{
+ return fileName;
+}
+
+void ConfigW::CreateFileWithBOM(void)
+{
+ // benski> this doesn't seem to be working on win9x
+ HANDLE hFile = CreateFile(fileName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
+ if (INVALID_HANDLE_VALUE == hFile)
+ {
+ WORD wBOM = 0xFEFF;
+ DWORD num = 0;
+ hFile = CreateFile(fileName, GENERIC_WRITE, 0, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL);
+ WriteFile(hFile, &wBOM, sizeof(WORD), &num, NULL);
+ emptyBOM = TRUE;
+ }
+ CloseHandle(hFile);
+}
+
+void ConfigW::RemoveEmptyFile(void)
+{
+ emptyBOM = FALSE;
+ HANDLE hFile = CreateFile(fileName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
+ if (INVALID_HANDLE_VALUE == hFile) return;
+ DWORD fsize;
+ fsize = GetFileSize(hFile, NULL);
+ CloseHandle(hFile);
+ if (fsize == 2) DeleteFile(fileName);
+ return;
+}
+
+BOOL ConfigW::IsFileExist(void)
+{
+ HANDLE hFile = CreateFile(fileName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
+ BOOL exist = (INVALID_HANDLE_VALUE != hFile);
+ if (exist)
+ {
+ exist = (GetFileSize(hFile, NULL) != 2);
+ CloseHandle(hFile);
+ }
+ return exist;
+} \ No newline at end of file
diff --git a/Src/Plugins/General/gen_crasher/config.h b/Src/Plugins/General/gen_crasher/config.h
new file mode 100644
index 00000000..1016b570
--- /dev/null
+++ b/Src/Plugins/General/gen_crasher/config.h
@@ -0,0 +1,62 @@
+#ifndef NULLSOFT_CONFIG_H_
+#define NULLSOFT_CONFIG_H_
+
+#include <windows.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <memory.h>
+
+#define BUFF_SIZE 8192
+
+class ConfigW
+{
+public:
+ ConfigW();
+ ConfigW(const wchar_t *ini, const wchar_t *section);
+ ~ConfigW();
+
+public:
+ void Flush(void);
+
+ BOOL Write(const wchar_t *name, double value);
+ BOOL Write(const wchar_t *section, const wchar_t *name, double value);
+ BOOL Write(const wchar_t *name, long long value);
+ BOOL Write(const wchar_t *section, const wchar_t *name, long long value);
+ BOOL Write(const wchar_t *name, int value);
+ BOOL Write(const wchar_t *section, const wchar_t *name, int value);
+ BOOL Write(const wchar_t *name, const wchar_t *value);
+ BOOL Write(const wchar_t *section, const wchar_t *name, const wchar_t *value);
+ BOOL Write(const wchar_t *name, const char value);
+ BOOL Write(const wchar_t *section, const wchar_t *name, const char *value);
+
+ int ReadInt(const wchar_t *name, int defvalue);
+ long long ReadInt64(const wchar_t *name, long long defvalue);
+ double ReadDouble(const wchar_t *name, double defvalue);
+ const char* ReadStringA(const wchar_t *name, const char *defvalue);
+ const wchar_t* ReadStringW(const wchar_t *name, const wchar_t *defvalue);
+ int ReadInt(const wchar_t *section, const wchar_t *name, int defvalue);
+ long long ReadInt64(const wchar_t *section, const wchar_t *name, long long defvalue);
+ double ReadDouble(const wchar_t *section, const wchar_t *name, double defvalue);
+ const char* ReadStringA(const wchar_t *section, const wchar_t *name, const char *defvalue);
+ const wchar_t* ReadStringW(const wchar_t *section, const wchar_t *name, const wchar_t *defvalue);
+
+ BOOL SetSection(const wchar_t *section);
+ BOOL SetIniFile(const wchar_t *file);
+ BOOL IsFileExist(void);
+
+ const wchar_t* GetSection(void);
+ const wchar_t* GetFile(void);
+private:
+ HANDLE CreateFileHandle();
+ void CreateFileWithBOM(void);
+ void RemoveEmptyFile(void);
+private:
+ BOOL emptyBOM;
+ wchar_t buff[BUFF_SIZE];
+ char *buffA;
+ wchar_t *fileName;
+ wchar_t *defSection;
+};
+
+#endif //NULLSOFT_CONFIG_H_ \ No newline at end of file
diff --git a/Src/Plugins/General/gen_crasher/configDlg.cpp b/Src/Plugins/General/gen_crasher/configDlg.cpp
new file mode 100644
index 00000000..5e845425
--- /dev/null
+++ b/Src/Plugins/General/gen_crasher/configDlg.cpp
@@ -0,0 +1,457 @@
+#include ".\configdlg.h"
+#include ".\smtpdlg.h"
+#include ".\resource.h"
+#include <shlobj.h>
+#include ".\miniVersion.h"
+#include ".\getwinver.h"
+#include ".\settings.h"
+#include ".\minidump.h"
+#include ".\main.h"
+#include <strsafe.h>
+#include "api__gen_crasher.h"
+
+extern Settings settings;
+
+BOOL CALLBACK ConfigDlgProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+ static wchar_t *path;
+
+ switch (uMsg)
+ {
+ case WM_INITDIALOG:
+ {
+ HWND hwCombo = GetDlgItem(hwndDlg, IDC_CMB_DMPTYPE);
+
+ // detect windows version
+ wchar_t strBuff[2048] = {0}, winVer[32] = {0}, build[32] = {0};
+ int nWinVer = 0;
+ GetWinVer(winVer, &nWinVer, build);
+ StringCchPrintf(strBuff, 2048, L"%s (%s)", winVer, build);
+ SetWindowText(GetDlgItem(hwndDlg, IDC_LBL_OSVERSION), strBuff);
+
+ // discover dbghlp.dll
+ HMODULE hm = NULL;
+ // first in app folder
+ if (GetModuleFileName( NULL, strBuff, _MAX_PATH ))
+ {
+ wchar_t *pSlash = wcsrchr( strBuff, L'\\' );
+ if (pSlash)
+ {
+ StringCchCopy(pSlash+1, 2048 - (pSlash + 1 - strBuff), L"dbghelp.dll" );
+ hm = LoadLibraryW( strBuff );
+ }
+ }
+ if (!hm)
+ {
+ // load any version we can
+ hm = LoadLibraryW(L"dbghelp.dll");
+ }
+
+ if (hm)
+ {
+ GetModuleFileName(hm, strBuff, 2048);
+ SetWindowText(GetDlgItem(hwndDlg, IDC_LBL_DLLPATH), strBuff);
+ // try to get dll version
+ CMiniVersion ver(strBuff);
+ if(ver.Init())
+ {
+ WORD dwBuf[4] = {0};
+ ver.GetProductVersion(dwBuf);
+ StringCchPrintf(strBuff, 2048, L"%d.%d.%d.%d", dwBuf[0], dwBuf[1], dwBuf[2], dwBuf[3]);
+ }
+ else
+ {
+ WASABI_API_LNGSTRINGW_BUF(IDS_UNABLE_TO_LOAD, strBuff, 128);
+ }
+ ver.Release();
+
+ BOOL (WINAPI* MiniDumpWriteDump)(
+ HANDLE hProcess,
+ DWORD ProcessId,
+ HANDLE hFile,
+ MINIDUMP_TYPE DumpType,
+ PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam,
+ PMINIDUMP_EXCEPTION_INFORMATION UserStreamParam,
+ PMINIDUMP_EXCEPTION_INFORMATION CallbackParam
+ ) = NULL;
+ *(FARPROC*)&MiniDumpWriteDump = GetProcAddress(hm, "MiniDumpWriteDump");
+
+ wchar_t temp[256] = {0};
+ StringCchPrintfW(temp, 256, L"%s [%s]", strBuff, WASABI_API_LNGSTRINGW(MiniDumpWriteDump ? IDS_LOADED_OK : IDS_UNABLE_TO_LOAD));
+ SetDlgItemText(hwndDlg, IDC_LBL_DLLVERSION, temp);
+
+ FreeLibrary(hm);
+ }
+ else
+ {
+ SetWindowText(GetDlgItem(hwndDlg, IDC_LBL_DLLPATH), WASABI_API_LNGSTRINGW(IDS_NOT_FOUND));
+ wchar_t temp[256] = {0}, temp2[128] = {0};
+ StringCchPrintfW(temp, 256, L"%s [%s]", WASABI_API_LNGSTRINGW(IDS_UNKNOWN), WASABI_API_LNGSTRINGW_BUF(IDS_UNABLE_TO_LOAD, temp2, 128));
+ SetWindowText(GetDlgItem(hwndDlg, IDC_LBL_DLLVERSION), temp);
+ }
+ // set combobox with values
+ WPARAM pos;
+ pos = SendMessage(hwCombo, CB_ADDSTRING, 0, (LPARAM) L"MiniDumpNormal");
+ SendMessage(hwCombo, CB_SETITEMDATA, pos, 0x00000000);
+ pos = SendMessage(hwCombo, CB_ADDSTRING, 0, (LPARAM) L"MiniDumpWithDataSegs");
+ SendMessage(hwCombo, CB_SETITEMDATA, pos, 0x00000001);
+ pos = SendMessage(hwCombo, CB_ADDSTRING, 0, (LPARAM) L"MiniDumpWithFullMemory");
+ SendMessage(hwCombo, CB_SETITEMDATA, pos, 0x00000002);
+ pos = SendMessage(hwCombo, CB_ADDSTRING, 0, (LPARAM) L"MiniDumpWithHandleData (Not Supported: Windows Me/98/95)");
+ SendMessage(hwCombo, CB_SETITEMDATA, pos, 0x00000004);
+ pos = SendMessage(hwCombo, CB_ADDSTRING, 0, (LPARAM) L"MiniDumpFilterMemory");
+ SendMessage(hwCombo, CB_SETITEMDATA, pos, 0x00000008);
+ pos = SendMessage(hwCombo, CB_ADDSTRING, 0, (LPARAM) L"MiniDumpScanMemory");
+ SendMessage(hwCombo, CB_SETITEMDATA, pos, 0x00000010);
+ pos = SendMessage(hwCombo, CB_ADDSTRING, 0, (LPARAM) L"MiniDumpWithUnloadedModules (Not Supported: DbgHelp 5.1 and earlier)");
+ SendMessage(hwCombo, CB_SETITEMDATA, pos, 0x00000020);
+ pos = SendMessage(hwCombo, CB_ADDSTRING, 0, (LPARAM) L"MiniDumpWithIndirectlyReferencedMemory (Not Supported: DbgHelp 5.1 and earlier)");
+ SendMessage(hwCombo, CB_SETITEMDATA, pos, 0x00000040);
+ pos = SendMessage(hwCombo, CB_ADDSTRING, 0, (LPARAM) L"MiniDumpFilterModulePaths (Not Supported: DbgHelp 5.1 and earlier)");
+ SendMessage(hwCombo, CB_SETITEMDATA, pos, 0x00000080);
+ pos = SendMessage(hwCombo, CB_ADDSTRING, 0, (LPARAM) L"MiniDumpWithProcessThreadData (Not Supported: DbgHelp 5.1 and earlier)");
+ SendMessage(hwCombo, CB_SETITEMDATA, pos, 0x00000100);
+ pos = SendMessage(hwCombo, CB_ADDSTRING, 0, (LPARAM) L"MiniDumpWithPrivateReadWriteMemory (Not Supported: DbgHelp 5.1 and earlier)");
+ SendMessage(hwCombo, CB_SETITEMDATA, pos, 0x00000200);
+ pos = SendMessage(hwCombo, CB_ADDSTRING, 0, (LPARAM) L"MiniDumpWithoutOptionalData (Not Supported: DbgHelp 6.1 and earlier)");
+ SendMessage(hwCombo, CB_SETITEMDATA, pos, 0x00000400);
+ pos = SendMessage(hwCombo, CB_ADDSTRING, 0, (LPARAM) L"MiniDumpWithFullMemoryInfo (Not Supported: DbgHelp 6.1 and earlier)");
+ SendMessage(hwCombo, CB_SETITEMDATA, pos, 0x00000800);
+ pos = SendMessage(hwCombo, CB_ADDSTRING, 0, (LPARAM) L"MiniDumpWithThreadInfo (Not Supported: DbgHelp 6.1 and earlier)");
+ SendMessage(hwCombo, CB_SETITEMDATA, pos, 0x00001000);
+ pos = SendMessage(hwCombo, CB_ADDSTRING, 0, (LPARAM) L"MiniDumpWithCodeSegs (Not Supported: DbgHelp 6.1 and earlier)");
+ SendMessage(hwCombo, CB_SETITEMDATA, pos, 0x00002000);
+
+ // read settings
+ settings.Load();
+ CheckDlgButton(hwndDlg, IDC_CHK_CREATELOG, settings.createLOG);
+ CheckDlgButton(hwndDlg, IDC_CHK_CREATEDMP, settings.createDMP);
+ CheckDlgButton(hwndDlg, IDC_CHK_RESTART, settings.autoRestart);
+ CheckDlgButton(hwndDlg, IDC_CHK_SILENT, settings.silentMode);
+ CheckDlgButton(hwndDlg, IDC_CHK_SEND, settings.sendData);
+ CheckDlgButton(hwndDlg, IDC_CHK_COMPRESS, settings.zipData);
+ CheckDlgButton(hwndDlg, IDC_RB_USECLIENT, settings.sendByClient);
+ CheckDlgButton(hwndDlg, IDC_RB_USESMTP, settings.sendBySMTP);
+
+ CreatePathFromFullName(&path, settings.zipPath);
+ SetWindowText(GetDlgItem(hwndDlg, IDC_EDT_PATH), path);
+
+ SetWindowText(GetDlgItem(hwndDlg, IDC_EDT_ZIPNAME), GetFileName(settings.zipPath));
+
+ SelectComboBoxItem(hwCombo, settings.dumpType);
+ SetWindowText(GetDlgItem(hwndDlg, IDC_EDT_DMPPATH), GetFileName(settings.dumpPath));
+
+ CheckDlgButton(hwndDlg, IDC_CHK_LOGSYSTEM, settings.logSystem);
+ CheckDlgButton(hwndDlg, IDC_CHK_LOGREGISTRY, settings.logRegistry);
+ CheckDlgButton(hwndDlg, IDC_CHK_LOGSTACK, settings.logStack);
+ CheckDlgButton(hwndDlg, IDC_CHK_LOGMODULE, settings.logModule);
+ SetWindowText(GetDlgItem(hwndDlg, IDC_EDT_LOGPATH), GetFileName(settings.logPath));
+
+ UpdateSend(hwndDlg, settings.sendData);
+ UpdateZip(hwndDlg, settings.zipData);
+ UpdateCreateDmp(hwndDlg, settings.createDMP);
+ UpdateCreateLog(hwndDlg, settings.createLOG);
+ }
+ break;
+ case WM_DESTROY:
+ {
+ wchar_t buf[1024] = {0};
+ int len, pathlen;
+
+ HWND hwCombo;
+ settings.createLOG = (SendMessage(GetDlgItem(hwndDlg, IDC_CHK_CREATELOG), BM_GETCHECK, 0,0) == BST_CHECKED);
+ settings.createDMP = (SendMessage(GetDlgItem(hwndDlg, IDC_CHK_CREATEDMP), BM_GETCHECK, 0,0) == BST_CHECKED);
+ settings.autoRestart = (SendMessage(GetDlgItem(hwndDlg, IDC_CHK_RESTART), BM_GETCHECK, 0,0) == BST_CHECKED);
+ settings.silentMode = (SendMessage(GetDlgItem(hwndDlg, IDC_CHK_SILENT), BM_GETCHECK, 0,0) == BST_CHECKED);
+ settings.sendData = (SendMessage(GetDlgItem(hwndDlg, IDC_CHK_SEND), BM_GETCHECK, 0,0) == BST_CHECKED);
+ settings.sendByClient = (SendMessage(GetDlgItem(hwndDlg, IDC_RB_USECLIENT), BM_GETCHECK, 0,0) == BST_CHECKED);
+ settings.sendBySMTP = !settings.sendByClient;
+ settings.zipData = (SendMessage(GetDlgItem(hwndDlg, IDC_CHK_COMPRESS), BM_GETCHECK, 0,0) == BST_CHECKED);
+
+ len = GetWindowText(GetDlgItem(hwndDlg, IDC_EDT_PATH), buf, 1024);
+ if (path) free(path);
+ path = NULL;
+ if (len)
+ {
+ int cpyLen;
+ path = (wchar_t*)malloc((len +1)*2);
+ cpyLen = (buf[len-1] == L'\\') ? len -1: len;
+ StringCchCopyN(path, len + 1, buf, cpyLen);
+ }
+ pathlen = (path) ? (int)lstrlen(path) + 1 : 0;
+
+ if (settings.zipPath) free(settings.zipPath);
+ settings.zipPath = NULL;
+ len = GetWindowText(GetDlgItem(hwndDlg, IDC_EDT_ZIPNAME), buf, 1024);
+ if (len)
+ {
+ if (pathlen)
+ {
+ len += pathlen;
+ settings.zipPath = (wchar_t*)malloc((len + 1)*2);
+ StringCchPrintf(settings.zipPath, len+1, L"%s\\%s", path, buf);
+ }
+ else
+ {
+ settings.zipPath = (wchar_t*)malloc((len + 1)*2);
+ StringCchCopy(settings.zipPath, len+1, buf);
+ }
+ }
+ else // because path is based on the zipPath just write path
+ {
+ if (pathlen)
+ {
+ len = pathlen;
+ settings.zipPath = (wchar_t*)malloc((len + 1)*2);
+ StringCchPrintf(settings.zipPath, len+1, L"%s\\", path);
+ }
+ }
+
+ hwCombo = GetDlgItem(hwndDlg, IDC_CMB_DMPTYPE);
+ settings.dumpType = (int) SendMessage(hwCombo, CB_GETITEMDATA, SendMessage(hwCombo, CB_GETCURSEL, 0, 0), 0);
+
+ if (settings.dumpPath) free(settings.dumpPath);
+ settings.dumpPath = NULL;
+ len = GetWindowText(GetDlgItem(hwndDlg, IDC_EDT_DMPNAME), buf, 1024);
+ if (len)
+ {
+ if (pathlen)
+ {
+ len += pathlen;
+ settings.dumpPath = (wchar_t*)malloc((len + 1)*2);
+ StringCchPrintf(settings.dumpPath, len+1, L"%s\\%s", path, buf);
+ }
+ else
+ {
+ settings.dumpPath = (wchar_t*)malloc((len + 1)*2);
+ StringCchCopy(settings.dumpPath, len+1, buf);
+ }
+ }
+
+ settings.logSystem = (SendMessage(GetDlgItem(hwndDlg, IDC_CHK_LOGSYSTEM), BM_GETCHECK, 0,0) == BST_CHECKED);
+ settings.logRegistry = (SendMessage(GetDlgItem(hwndDlg, IDC_CHK_LOGREGISTRY), BM_GETCHECK, 0,0) == BST_CHECKED);
+ settings.logStack = (SendMessage(GetDlgItem(hwndDlg, IDC_CHK_LOGSTACK), BM_GETCHECK, 0,0) == BST_CHECKED);
+ settings.logModule = (SendMessage(GetDlgItem(hwndDlg, IDC_CHK_LOGMODULE), BM_GETCHECK, 0,0) == BST_CHECKED);
+
+ if (settings.logPath) free(settings.logPath);
+ settings.logPath = NULL;
+ len = GetWindowText(GetDlgItem(hwndDlg, IDC_EDT_LOGNAME), buf, 1024);
+ if (len)
+ {
+ if (pathlen)
+ {
+ len += pathlen;
+ settings.logPath = (wchar_t*)malloc((len + 1)*2);
+ StringCchPrintf(settings.logPath, len+1, L"%s\\%s", path, buf);
+ }
+ else
+ {
+ settings.logPath = (wchar_t*)malloc((len + 1)*2);
+ StringCchCopy(settings.logPath, len+1, buf);
+ }
+ }
+
+ if (!settings.Save())
+ {
+ wchar_t title[32] = {0};
+ MessageBox(NULL, WASABI_API_LNGSTRINGW(IDS_UNABLE_TO_SAVE_SETTINGS),
+ WASABI_API_LNGSTRINGW_BUF(IDS_SAVE_ERROR,title,32), MB_OK);
+ }
+
+ if(path) free(path);
+ path = NULL;
+ break;
+ }
+ case WM_COMMAND:
+ int ctrl = LOWORD(wParam);
+ switch (ctrl)
+ {
+ case IDC_CHK_CREATELOG:
+ case IDC_CHK_CREATEDMP:
+ case IDC_CHK_SEND:
+ case IDC_CHK_COMPRESS:
+ if (HIWORD(wParam) == BN_CLICKED)
+ {
+ BOOL enabled = (SendMessage((HWND) lParam, BM_GETCHECK, 0,0) == BST_CHECKED);
+ if (ctrl == IDC_CHK_CREATELOG) UpdateCreateLog(hwndDlg, enabled);
+ else if (ctrl == IDC_CHK_CREATEDMP) UpdateCreateDmp(hwndDlg, enabled);
+ else if (ctrl == IDC_CHK_SEND) UpdateSend(hwndDlg, enabled);
+ else if (ctrl == IDC_CHK_COMPRESS) UpdateZip(hwndDlg, enabled);
+ }
+ break;
+ case IDC_RB_USESMTP:
+ case IDC_RB_USECLIENT:
+ if (HIWORD(wParam) == BN_CLICKED) UpdateSendType(hwndDlg, (ctrl == IDC_RB_USESMTP));
+ break;
+ case IDC_BTN_PATH:
+ {
+ wchar_t szFile[2048] = {0}; // buffer for file name
+ GetWindowText(GetDlgItem(hwndDlg, IDC_EDT_PATH), szFile, 2048);
+ if (OpenFolderDialog(NULL, szFile))SetWindowText(GetDlgItem(hwndDlg, IDC_EDT_PATH), szFile);
+ }
+ break;
+ case IDC_BTN_SMTP:
+ WASABI_API_DIALOGBOXW(IDD_DLG_SMTP, hwndDlg, (DLGPROC)smtpDlgProc);
+ break;
+ }
+ break;
+ }
+ //settings.smtpUser;
+ return FALSE;
+}
+
+int SelectComboBoxItem(const HWND hwCombo, int data)
+{
+ int count = (int) SendMessage(hwCombo, CB_GETCOUNT , 0, 0);
+ for (int i = 0; i < count; i++)
+ {
+ if (data == (int) SendMessage(hwCombo, CB_GETITEMDATA, i, 0))
+ return (int) SendMessage(hwCombo, CB_SETCURSEL, i, 0);
+ }
+ return -1;
+}
+
+void UpdateZip(HWND hwndDlg, BOOL enabled)
+{
+ EnableWindow(GetDlgItem(hwndDlg, IDC_GRP_ZIP), enabled);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_LBL_ZIPNAME), enabled);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_EDT_ZIPNAME), enabled);
+}
+
+void UpdateSendType(HWND hwndDlg, BOOL enabled)
+{
+ EnableWindow(GetDlgItem(hwndDlg, IDC_BTN_SMTP), enabled);
+}
+
+void UpdateSend(HWND hwndDlg, BOOL enabled)
+{
+ EnableWindow(GetDlgItem(hwndDlg, IDC_RB_USECLIENT), enabled);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_RB_USESMTP), enabled);
+ BOOL stmpPressed = (SendMessage(GetDlgItem(hwndDlg,IDC_RB_USESMTP), BM_GETCHECK, 0,0) == BST_CHECKED);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_BTN_SMTP), enabled && stmpPressed);
+}
+
+void UpdateCreateDmp(HWND hwndDlg, BOOL enabled)
+{
+ EnableWindow(GetDlgItem(hwndDlg, IDC_LBL_OSVERSION_CAPTION), enabled);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_LBL_DLLPATH_CAPTION), enabled);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_LBL_DLLVERSION_CAPTION), enabled);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_LBL_OSVERSION), enabled);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_LBL_DLLPATH), enabled);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_LBL_DLLVERSION), enabled);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_LBL_DMPTYPE), enabled);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_CMB_DMPTYPE), enabled);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_LBL_DMPNAME), enabled);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_EDT_DMPNAME), enabled);
+}
+
+void UpdateCreateLog(HWND hwndDlg, BOOL enabled)
+{
+ EnableWindow(GetDlgItem(hwndDlg, IDC_GRP_LOG), enabled);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_CHK_LOGSYSTEM), enabled);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_CHK_LOGREGISTRY), enabled);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_CHK_LOGSTACK), enabled);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_CHK_LOGMODULE), enabled);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_LBL_LOGNAME), enabled);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_EDT_LOGNAME), enabled);
+}
+
+BOOL CALLBACK browseEnumProc(HWND hwnd, LPARAM lParam)
+{
+ wchar_t cl[32] = {0};
+ GetClassNameW(hwnd, cl, ARRAYSIZE(cl));
+ if (!lstrcmpiW(cl, WC_TREEVIEW))
+ {
+ PostMessage(hwnd, TVM_ENSUREVISIBLE, 0, (LPARAM)TreeView_GetSelection(hwnd));
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static int CALLBACK WINAPI BrowseCallbackProc( HWND hwnd, UINT uMsg, LPARAM lParam, LPARAM lpData)
+{
+ switch (uMsg)
+ {
+ case BFFM_INITIALIZED:
+ {
+ //SetWindowText(hwnd, getString(IDS_P_SELECT_LANGDIR,NULL,0));
+ SendMessage(hwnd, BFFM_SETSELECTION, 1, (LPARAM)lpData);
+
+ // this is not nice but it fixes the selection not working correctly on all OSes
+ EnumChildWindows(hwnd, browseEnumProc, 0);
+ }
+ return 0;
+ }
+ return 0;
+}
+
+BOOL OpenFolderDialog(HWND parent, LPWSTR pathBuffer)
+{
+ if (!pathBuffer) return FALSE;
+
+ // Return value for the function
+ BOOL ret = TRUE;
+
+ // Set up the params
+ BROWSEINFO browseInfo = {0};
+ browseInfo.hwndOwner = parent;
+ browseInfo.lpfn = BrowseCallbackProc;
+ browseInfo.lParam = (LPARAM)pathBuffer;
+ browseInfo.lpszTitle = WASABI_API_LNGSTRINGW(IDS_SELECT_FOLDER_FOR_ERROR_INFO);
+ browseInfo.ulFlags = BIF_NEWDIALOGSTYLE;
+
+ // Show the dialog
+ LPITEMIDLIST itemIDList = SHBrowseForFolder(&browseInfo);
+
+ // Did user press cancel?
+ if (!itemIDList)
+ ret = FALSE;
+
+ // Is everything so far?
+ if (ret != FALSE)
+ {
+ // Get the path from the returned ITEMIDLIST
+ if (!SHGetPathFromIDList(itemIDList, pathBuffer))
+ ret = FALSE;
+
+ // Now we need to free the ITEMIDLIST the shell allocated
+ LPMALLOC shellMalloc;
+ HRESULT hr;
+
+ // Get pointer to the shell's malloc interface
+ hr = SHGetMalloc(&shellMalloc);
+
+ // Did it work?
+ if (SUCCEEDED(hr))
+ {
+ shellMalloc->Free(itemIDList);
+ shellMalloc->Release();
+ ret = TRUE;
+ }
+ }
+ return ret;
+}
+
+void CreatePathFromFullName(wchar_t **path, const wchar_t *fullname)
+{
+ if (*path) free(*path);
+ *path = NULL;
+ if (!fullname) return;
+ const wchar_t *end = wcsrchr(fullname, L'\\');
+ if (!end || end == fullname) return;
+ int len = (int)(end - fullname + 1);
+ if (len == 3) len++;
+
+ *path = (wchar_t*)malloc(len*2);
+ StringCchCopyN(*path, len, fullname, len -1);
+}
+
+const wchar_t* GetFileName(const wchar_t *fullname)
+{
+ if (!fullname) return NULL;
+ const wchar_t *start = wcsrchr(fullname, L'\\');
+ if (start && start != fullname) start = CharNext(start);
+
+ return start;
+} \ No newline at end of file
diff --git a/Src/Plugins/General/gen_crasher/configDlg.h b/Src/Plugins/General/gen_crasher/configDlg.h
new file mode 100644
index 00000000..5c3ef4df
--- /dev/null
+++ b/Src/Plugins/General/gen_crasher/configDlg.h
@@ -0,0 +1,15 @@
+#pragma once
+#include <windows.h>
+
+int SelectComboBoxItem(const HWND hwCombo, int data);
+BOOL OpenFolderDialog(HWND parent, LPWSTR pathBuffer);
+BOOL CALLBACK ConfigDlgProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
+
+void UpdateSendType(HWND hwndDlg, BOOL enabled);
+void UpdateSend(HWND hwndDlg, BOOL enabled);
+void UpdateCreateDmp(HWND hwndDlg, BOOL enabled);
+void UpdateCreateLog(HWND hwndDlg, BOOL enabled);
+void UpdateZip(HWND hwndDlg, BOOL enabled);
+
+void CreatePathFromFullName(wchar_t **path, const wchar_t *fullname);
+const wchar_t* GetFileName(const wchar_t *fullname); \ No newline at end of file
diff --git a/Src/Plugins/General/gen_crasher/crashDlg.cpp b/Src/Plugins/General/gen_crasher/crashDlg.cpp
new file mode 100644
index 00000000..d74bee75
--- /dev/null
+++ b/Src/Plugins/General/gen_crasher/crashDlg.cpp
@@ -0,0 +1,101 @@
+#include ".\crashdlg.h"
+#include ".\configdlg.h"
+#include ".\resource.h"
+#include ".\settings.h"
+#include "exceptionhandler.h"
+#include <strsafe.h>
+
+extern Settings settings;
+extern PEXCEPTION_POINTERS gExceptionInfo;
+
+BOOL CALLBACK CrashDlgProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+ UNREFERENCED_PARAMETER(lParam);
+ switch (uMsg)
+ {
+ case WM_INITDIALOG:
+ {
+ // as we're loading things, make sure we've got a decent icon size to use in the second usage
+ HICON hIcon = (HICON)LoadImage(GetModuleHandle(NULL),MAKEINTRESOURCE(102),IMAGE_ICON,48,48,LR_SHARED);
+ SetClassLongPtr(hwndDlg, GCLP_HICON, (LONG_PTR)hIcon);
+
+ HWND hwndPrg = GetDlgItem(hwndDlg, IDC_PRG_COLLECT);
+ SendMessage(hwndPrg, PBM_SETRANGE, 0, MAKELPARAM(0,100));
+ SendMessage(hwndPrg, PBM_SETPOS, 0, 0);
+
+ // this will make sure that we've got the logo shown even when using a localised version
+ SendDlgItemMessage(hwndDlg,IDC_BMP_LOGO,STM_SETIMAGE,IMAGE_ICON,(LPARAM)hIcon);
+
+ SetDlgItemText(hwndDlg, IDC_LBL_STEP, L"Analyzing settings...");
+ settings.ClearTempData();
+
+ wchar_t waPath[2*_MAX_PATH] = {0};
+ if (GetModuleFileName( NULL, waPath, 2*_MAX_PATH ))
+ {
+ settings.WriteWinamp(waPath);
+ }
+
+ SetTimer(hwndDlg, 123, 1000, NULL);
+ break;
+ }
+ case WM_TIMER:
+ if (wParam == 123)
+ {
+ KillTimer(hwndDlg,wParam);
+ HWND hwndPrg = GetDlgItem(hwndDlg, IDC_PRG_COLLECT);
+ SetDlgItemText(hwndDlg, IDC_LBL_STEP, L"Generating log file...");
+ SendMessage(hwndPrg, PBM_SETPOS, 30, 0);
+ UpdateWindow(hwndDlg);
+ if (settings.createLOG) settings.WriteLogCollectResult(CreateLog(gExceptionInfo, L"Winamp"));
+ SetDlgItemText(hwndDlg, IDC_LBL_STEP, L"Generating dump file...");
+ SendMessage(hwndPrg, PBM_SETPOS, 50, 0);
+ UpdateWindow(hwndDlg);
+ if (settings.createDMP) settings.WriteDmpCollectResult(CreateDump(gExceptionInfo));
+ SetDlgItemText(hwndDlg, IDC_LBL_STEP, L"Starting error reporter...");
+ SendMessage(hwndPrg, PBM_SETPOS, 90, 0);
+ UpdateWindow(hwndDlg);
+ STARTUPINFO si = {0};
+ si.cb = sizeof(si);
+ si.dwFlags = STARTF_USESHOWWINDOW;
+ si.wShowWindow = SW_SHOW;
+
+ PROCESS_INFORMATION pi = {0};
+ wchar_t reporter[512] = {0}, waPlugPath[MAX_PATH] = {0}, cmd[512] = {0}, *waPath = 0;
+ GetModuleFileName( NULL, waPlugPath, MAX_PATH);
+ CreatePathFromFullName(&waPath, waPlugPath);
+ StringCchPrintf(reporter, 512, L"%s\\reporter.exe", waPath);
+ StringCchPrintf(cmd, 512, L" \"%s\"", settings.GetPath());
+
+ if (CreateProcess(
+ reporter, // name of executable module
+ cmd, // command line string
+ NULL, // process attributes
+ NULL, // thread attributes
+ FALSE, // handle inheritance option
+ 0, // creation flags
+ NULL, // new environment block
+ NULL, // current directory name
+ &si, // startup information
+ &pi)) // process information
+ {
+ SetDlgItemText(hwndDlg, IDC_LBL_STEP, L"Done.");
+ SetTimer(hwndDlg, 126, 200, NULL);
+ }
+ else
+ {
+ SetDlgItemText(hwndDlg, IDC_LBL_STEP, L"Error. Unable to run reporter.");
+ SetTimer(hwndDlg, 126, 3000, NULL);
+ }
+
+ SendMessage(hwndPrg, PBM_SETPOS, 100, 0);
+ UpdateWindow(hwndDlg);
+ }
+ else if (wParam == 126)
+ {
+ KillTimer(hwndDlg,wParam);
+ DestroyWindow(hwndDlg);
+ }
+ break;
+ }
+ return FALSE;
+} \ No newline at end of file
diff --git a/Src/Plugins/General/gen_crasher/crashDlg.h b/Src/Plugins/General/gen_crasher/crashDlg.h
new file mode 100644
index 00000000..eb92f027
--- /dev/null
+++ b/Src/Plugins/General/gen_crasher/crashDlg.h
@@ -0,0 +1,5 @@
+#pragma once
+#include <windows.h>
+#include <commctrl.h>
+
+BOOL CALLBACK CrashDlgProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam); \ No newline at end of file
diff --git a/Src/Plugins/General/gen_crasher/feedback/email/SendEmail.cpp b/Src/Plugins/General/gen_crasher/feedback/email/SendEmail.cpp
new file mode 100644
index 00000000..20e2d435
--- /dev/null
+++ b/Src/Plugins/General/gen_crasher/feedback/email/SendEmail.cpp
@@ -0,0 +1,364 @@
+// SendEmail.cpp Version 1.0
+//
+// Author: Hans Dietrich
+// hdietrich2@hotmail.com
+//
+// This software is released into the public domain.
+// You are free to use it in any way you like, except
+// that you may not sell this source code.
+//
+// This software is provided "as is" with no expressed
+// or implied warranty. I accept no liability for any
+// damage or loss of business that this software may cause.
+//
+///////////////////////////////////////////////////////////////////////////////
+
+// Notes on compiling: this does not use MFC. Set precompiled header
+// option to "Not using precompiled headers".
+//
+// This code assumes that a default mail client has been set up.
+
+#include ".\sendemail.h"
+#include <crtdbg.h>
+#include <io.h>
+#pragma warning(disable: 4228)
+#include <mapi.h>
+#pragma warning(default: 4228)
+
+#pragma warning(disable:4127) // for _ASSERTE
+
+#ifndef _countof
+#define _countof(array) (sizeof(array)/sizeof(array[0]))
+#endif
+
+
+BOOL SendEmail(HWND hWnd, // parent window, must not be NULL
+ LPCTSTR lpszTo, // must NOT be NULL or empty
+ LPCTSTR lpszToName, // may be NULL
+ LPCTSTR lpszSubject, // may be NULL
+ LPCTSTR lpszMessage, // may be NULL
+ LPCTSTR lpszAttachment) // may be NULL
+{
+
+ _ASSERTE(lpszTo && lpszTo[0] != _T('\0'));
+ if (lpszTo == NULL || lpszTo[0] == _T('\0'))
+ return FALSE;
+
+ // ===== LOAD MAPI DLL =====
+
+ HMODULE hMapi = ::LoadLibraryA("MAPI32.DLL");
+
+ _ASSERTE(hMapi);
+ if (hMapi == NULL)
+ {
+ ::MessageBox(NULL,
+ _T("Failed to load MAPI32.DLL."),
+ _T("CrashRep"),
+ MB_OK|MB_ICONSTOP);
+ return FALSE;
+ }
+
+ // get proc address for MAPISendMail
+ ULONG (PASCAL *lpfnSendMail)(ULONG, ULONG, MapiMessage*, FLAGS, ULONG);
+ (FARPROC&)lpfnSendMail = GetProcAddress(hMapi, "MAPISendMail");
+ _ASSERTE(lpfnSendMail);
+ if (lpfnSendMail == NULL)
+ {
+ ::MessageBox(NULL,
+ _T("Invalid MAPI32.DLL, cannot find MAPISendMail."),
+ _T("CrashRep"),
+ MB_OK|MB_ICONSTOP);
+ ::FreeLibrary(hMapi);
+ return FALSE;
+ }
+
+ // ===== SET UP MAPI STRUCTS =====
+
+ // ===== file description (for the attachment) =====
+
+ MapiFileDesc fileDesc;
+ memset(&fileDesc, 0, sizeof(fileDesc));
+
+ // ----- attachment path
+ TCHAR szTempName[_MAX_PATH*2];
+ memset(szTempName, 0, sizeof(szTempName));
+ if (lpszAttachment && lpszAttachment[0] != _T('\0'))
+ lstrcpyn(szTempName, lpszAttachment, _countof(szTempName)-2);
+
+#ifdef _UNICODE
+ char szTempNameA[_MAX_PATH*2];
+ memset(szTempNameA, 0, sizeof(szTempNameA));
+ _wcstombsz(szTempNameA, szTempName, _countof(szTempNameA)-2);
+#endif
+
+ // ----- attachment title
+ TCHAR szTitle[_MAX_PATH*2];
+ memset(szTitle, 0, sizeof(szTitle));
+ if (lpszAttachment && lpszAttachment[0] != _T('\0'))
+ lstrcpyn(szTitle, GetFilePart(lpszAttachment), _countof(szTitle)-2);
+
+#ifdef _UNICODE
+ char szTitleA[_MAX_PATH*2];
+ memset(szTitleA, 0, sizeof(szTitleA));
+ _wcstombsz(szTitleA, szTitle, _countof(szTitleA)-2);
+#endif
+
+ fileDesc.nPosition = (ULONG)-1;
+#ifdef _UNICODE
+ fileDesc.lpszPathName = szTempNameA;
+ fileDesc.lpszFileName = szTitleA;
+#else
+ fileDesc.lpszPathName = szTempName;
+ fileDesc.lpszFileName = szTitle;
+#endif
+
+ // ===== recipient =====
+
+ MapiRecipDesc recip;
+ memset(&recip, 0, sizeof(recip));
+
+ // ----- name
+ TCHAR szRecipName[_MAX_PATH*2];
+ memset(szRecipName, 0, sizeof(szRecipName));
+ if (lpszToName && lpszToName[0] != _T('\0'))
+ lstrcpyn(szRecipName, lpszToName, _countof(szRecipName)-2);
+#ifdef _UNICODE
+ char szRecipNameA[_MAX_PATH*2];
+ memset(szRecipNameA, 0, sizeof(szRecipNameA));
+ _wcstombsz(szRecipNameA, szRecipName, _countof(szRecipNameA)-2);
+#endif
+
+ if (lpszToName && lpszToName[0] != _T('\0'))
+ {
+#ifdef _UNICODE
+ recip.lpszName = szRecipNameA;
+#else
+ recip.lpszName = szRecipName;
+#endif
+ }
+
+ // ----- address
+ TCHAR szAddress[_MAX_PATH*2];
+ memset(szAddress, 0, sizeof(szAddress));
+ lstrcpyn(szAddress, lpszTo, _countof(szAddress)-2);
+#ifdef _UNICODE
+ char szAddressA[_MAX_PATH*2];
+ memset(szAddressA, 0, sizeof(szAddressA));
+ _wcstombsz(szAddressA, szAddress, _countof(szAddressA)-2);
+#endif
+
+#ifdef _UNICODE
+ recip.lpszAddress = szAddressA;
+#else
+ recip.lpszAddress = szAddress;
+#endif
+
+ recip.ulRecipClass = MAPI_TO;
+
+ // ===== message =====
+
+ MapiMessage message;
+ memset(&message, 0, sizeof(message));
+
+ // ----- recipient
+ message.nRecipCount = 1;
+ message.lpRecips = &recip;
+
+ // ----- attachment
+ if (lpszAttachment && lpszAttachment[0] != _T('\0'))
+ {
+ message.nFileCount = 1;
+ message.lpFiles = &fileDesc;
+ }
+
+ // ----- subject
+ TCHAR szSubject[_MAX_PATH*2];
+ memset(szSubject, 0, sizeof(szSubject));
+ if (lpszSubject && lpszSubject[0] != _T('\0'))
+ lstrcpyn(szSubject, lpszSubject, _countof(szSubject)-2);
+#ifdef _UNICODE
+ char szSubjectA[_MAX_PATH*2];
+ memset(szSubjectA, 0, sizeof(szSubjectA));
+ _wcstombsz(szSubjectA, szSubject, _countof(szSubjectA)-2);
+#endif
+
+ if (lpszSubject && lpszSubject[0] != _T('\0'))
+ {
+#ifdef _UNICODE
+ message.lpszSubject = szSubjectA;
+#else
+ message.lpszSubject = szSubject;
+#endif
+ }
+
+ // ----- message
+ // message may be large, so allocate buffer
+ TCHAR *pszMessage = NULL;
+ int nMessageSize = 0;
+ if (lpszMessage)
+ {
+ nMessageSize = lstrlen(lpszMessage);
+ if (nMessageSize > 0)
+ {
+ pszMessage = new TCHAR [nMessageSize + 10];
+ _ASSERTE(pszMessage);
+ memset(pszMessage, 0, nMessageSize + 10);
+ lstrcpy(pszMessage, lpszMessage);
+ }
+ }
+
+ char *pszMessageA = NULL;
+#ifdef _UNICODE
+ if (nMessageSize > 0)
+ {
+ pszMessageA = new char [nMessageSize + 10];
+ _ASSERTE(pszMessageA);
+ memset(pszMessageA, 0, nMessageSize + 10);
+ }
+ _wcstombsz(pszMessageA, pszMessage, nMessageSize+2);
+#endif
+
+ if (nMessageSize > 0)
+ {
+#ifdef _UNICODE
+ message.lpszNoteText = pszMessageA;
+#else
+ message.lpszNoteText = pszMessage;
+#endif
+ }
+
+
+ // ===== SETUP FINISHED, READY TO SEND =====
+
+
+ // some extra precautions are required to use MAPISendMail as it
+ // tends to enable the parent window in between dialogs (after
+ // the login dialog, but before the send note dialog).
+
+ ::SetCapture(hWnd);
+ ::SetFocus(NULL);
+ ::EnableWindow(hWnd, FALSE);
+
+ ULONG nError = lpfnSendMail(0,
+ (LPARAM)hWnd,
+ &message,
+ MAPI_LOGON_UI | MAPI_DIALOG,
+ 0);
+
+#ifdef _DEBUG
+ TCHAR *cp = NULL;
+ switch (nError)
+ {
+ case SUCCESS_SUCCESS: cp = _T("SUCCESS_SUCCESS"); break;
+ case MAPI_E_USER_ABORT: cp = _T("MAPI_E_USER_ABORT"); break;
+ case MAPI_E_FAILURE: cp = _T("MAPI_E_FAILURE"); break;
+ case MAPI_E_LOGON_FAILURE: cp = _T("MAPI_E_LOGON_FAILURE"); break;
+ case MAPI_E_DISK_FULL: cp = _T("MAPI_E_DISK_FULL"); break;
+ case MAPI_E_INSUFFICIENT_MEMORY: cp = _T("MAPI_E_INSUFFICIENT_MEMORY"); break;
+ case MAPI_E_ACCESS_DENIED: cp = _T("MAPI_E_ACCESS_DENIED"); break;
+ case MAPI_E_TOO_MANY_SESSIONS: cp = _T("MAPI_E_TOO_MANY_SESSIONS"); break;
+ case MAPI_E_TOO_MANY_FILES: cp = _T("MAPI_E_TOO_MANY_FILES"); break;
+ case MAPI_E_TOO_MANY_RECIPIENTS: cp = _T("MAPI_E_TOO_MANY_RECIPIENTS"); break;
+ case MAPI_E_ATTACHMENT_NOT_FOUND: cp = _T("MAPI_E_ATTACHMENT_NOT_FOUND"); break;
+ case MAPI_E_ATTACHMENT_OPEN_FAILURE: cp = _T("MAPI_E_ATTACHMENT_OPEN_FAILURE"); break;
+ case MAPI_E_ATTACHMENT_WRITE_FAILURE: cp = _T("MAPI_E_ATTACHMENT_WRITE_FAILURE"); break;
+ case MAPI_E_UNKNOWN_RECIPIENT: cp = _T("MAPI_E_UNKNOWN_RECIPIENT"); break;
+ case MAPI_E_BAD_RECIPTYPE: cp = _T("MAPI_E_BAD_RECIPTYPE"); break;
+ case MAPI_E_NO_MESSAGES: cp = _T("MAPI_E_NO_MESSAGES"); break;
+ case MAPI_E_INVALID_MESSAGE: cp = _T("MAPI_E_INVALID_MESSAGE"); break;
+ case MAPI_E_TEXT_TOO_LARGE: cp = _T("MAPI_E_TEXT_TOO_LARGE"); break;
+ case MAPI_E_INVALID_SESSION: cp = _T("MAPI_E_INVALID_SESSION"); break;
+ case MAPI_E_TYPE_NOT_SUPPORTED: cp = _T("MAPI_E_TYPE_NOT_SUPPORTED"); break;
+ case MAPI_E_AMBIGUOUS_RECIPIENT: cp = _T("MAPI_E_AMBIGUOUS_RECIPIENT"); break;
+ case MAPI_E_MESSAGE_IN_USE: cp = _T("MAPI_E_MESSAGE_IN_USE"); break;
+ case MAPI_E_NETWORK_FAILURE: cp = _T("MAPI_E_NETWORK_FAILURE"); break;
+ case MAPI_E_INVALID_EDITFIELDS: cp = _T("MAPI_E_INVALID_EDITFIELDS"); break;
+ case MAPI_E_INVALID_RECIPS: cp = _T("MAPI_E_INVALID_RECIPS"); break;
+ case MAPI_E_NOT_SUPPORTED: cp = _T("MAPI_E_NOT_SUPPORTED"); break;
+ default: cp = _T("unknown error"); break;
+ }
+
+ if (nError == SUCCESS_SUCCESS)
+ {
+ OutputDebugString(_T("MAPISendMail ok\r\n"));
+ }
+ else
+ {
+ OutputDebugString(L"ERROR - MAPISendMail failed: ");
+ OutputDebugString(cp);
+ OutputDebugString(L"\r\n");
+
+ }
+#endif // _DEBUG
+
+
+ // ===== SEND COMPLETE, CLEAN UP =====
+
+ // after returning from the MAPISendMail call, the window must be
+ // re-enabled and focus returned to the frame to undo the workaround
+ // done before the MAPI call.
+
+ ::ReleaseCapture();
+ ::EnableWindow(hWnd, TRUE);
+ ::SetActiveWindow(NULL);
+ ::SetActiveWindow(hWnd);
+ ::SetFocus(hWnd);
+
+ if (pszMessage)
+ delete [] pszMessage;
+ pszMessage = NULL;
+
+ if (pszMessageA)
+ delete [] pszMessageA;
+ pszMessageA = NULL;
+
+ if (hMapi)
+ ::FreeLibrary(hMapi);
+ hMapi = NULL;
+
+ BOOL bRet = TRUE;
+ if (nError != SUCCESS_SUCCESS) // &&
+ //nError != MAPI_USER_ABORT &&
+ //nError != MAPI_E_LOGIN_FAILURE)
+ {
+ bRet = FALSE;
+ }
+
+ return bRet;
+}
+
+const wchar_t* GetFilePart(LPCWSTR lpszFile)
+{
+ const wchar_t *result = wcsrchr(lpszFile, _T('\\'));
+ if (result)
+ result++;
+ else
+ result = (wchar_t *) lpszFile;
+ return result;
+}
+
+int xwcstombsz(char* mbstr, const wchar_t* wcstr, size_t count)
+{
+ if (count == 0 && mbstr != NULL)
+ return 0;
+
+ int result = ::WideCharToMultiByte(CP_ACP, 0, wcstr, -1,
+ mbstr, (int)count, NULL, NULL);
+ _ASSERTE(mbstr == NULL || result <= (int)count);
+ if (result > 0)
+ mbstr[result-1] = 0;
+ return result;
+}
+
+int xmbstowcsz(wchar_t* wcstr, const char* mbstr, size_t count)
+{
+ if (count == 0 && wcstr != NULL)
+ return 0;
+
+ int result = ::MultiByteToWideChar(CP_ACP, 0, mbstr, -1,
+ wcstr, (int)count);
+ _ASSERTE(wcstr == NULL || result <= (int)count);
+ if (result > 0)
+ wcstr[result-1] = 0;
+ return result;
+} \ No newline at end of file
diff --git a/Src/Plugins/General/gen_crasher/feedback/email/SendEmail.h b/Src/Plugins/General/gen_crasher/feedback/email/SendEmail.h
new file mode 100644
index 00000000..ce1c523b
--- /dev/null
+++ b/Src/Plugins/General/gen_crasher/feedback/email/SendEmail.h
@@ -0,0 +1,38 @@
+// SendEmail.h Version 1.0
+//
+// Author: Hans Dietrich
+// hdietrich2@hotmail.com
+//
+// This software is released into the public domain.
+// You are free to use it in any way you like, except
+// that you may not sell this source code.
+//
+// This software is provided "as is" with no expressed
+// or implied warranty. I accept no liability for any
+// damage or loss of business that this software may cause.
+//
+///////////////////////////////////////////////////////////////////////////////
+
+#ifndef SENDEMAIL_H
+#define SENDEMAIL_H
+
+#include <tchar.h>
+#include <windows.h>
+
+const wchar_t* GetFilePart(LPCWSTR lpszFile);
+
+BOOL SendEmail(HWND hWnd, // parent window, must not be NULL
+ LPCTSTR lpszTo, // must NOT be NULL or empty
+ LPCTSTR lpszToName, // may be NULL
+ LPCTSTR lpszSubject, // may be NULL
+ LPCTSTR lpszMessage, // may be NULL
+ LPCTSTR lpszAttachment); // may be NULL
+
+
+#define _wcstombsz xwcstombsz
+#define _mbstowcsz xmbstowcsz
+
+int xwcstombsz(char* mbstr, const wchar_t* wcstr, size_t count);
+int xmbstowcsz(wchar_t* wcstr, const char* mbstr, size_t count);
+
+#endif //SENDEMAIL_H \ No newline at end of file
diff --git a/Src/Plugins/General/gen_crasher/feedback/feedback.rc b/Src/Plugins/General/gen_crasher/feedback/feedback.rc
new file mode 100644
index 00000000..0e02c7e1
--- /dev/null
+++ b/Src/Plugins/General/gen_crasher/feedback/feedback.rc
@@ -0,0 +1,119 @@
+// Microsoft Visual C++ generated resource script.
+//
+#include "resource.h"
+
+#define APSTUDIO_READONLY_SYMBOLS
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 2 resource.
+//
+#define APSTUDIO_HIDDEN_SYMBOLS
+#include "windows.h"
+#undef APSTUDIO_HIDDEN_SYMBOLS
+
+/////////////////////////////////////////////////////////////////////////////
+#undef APSTUDIO_READONLY_SYMBOLS
+
+/////////////////////////////////////////////////////////////////////////////
+// English (U.S.) resources
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
+#ifdef _WIN32
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
+#pragma code_page(1252)
+#endif //_WIN32
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Icon
+//
+
+// Icon with lowest ID value placed first to ensure application icon
+// remains consistent on all systems.
+ICON_XP ICON "..\\..\\..\\..\\Winamp\\resource\\WinampIcon.ico"
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Dialog
+//
+
+IDD_DLG_SILENT DIALOGEX 0, 0, 187, 42
+STYLE DS_SYSMODAL | DS_SETFONT | DS_SETFOREGROUND | DS_FIXEDSYS | DS_CENTER | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU
+EXSTYLE WS_EX_APPWINDOW
+CAPTION "Winamp Error Reporter"
+FONT 8, "MS Shell Dlg", 400, 0, 0x1
+BEGIN
+ ICON ICON_XP,IDC_STATIC,8,6,21,20,SS_REALSIZEIMAGE
+ LTEXT "",IDC_LBL_STEP,48,6,127,10
+ CONTROL "",IDC_PRG_COLLECT,"msctls_progress32",WS_BORDER,49,19,127,9
+ PUSHBUTTON "&Open Report Folder...",IDC_BUTTON1,47,21,87,13,NOT WS_VISIBLE
+ DEFPUSHBUTTON "&Close",IDC_BUTTON2,138,21,39,13,NOT WS_VISIBLE
+END
+
+
+#ifdef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// TEXTINCLUDE
+//
+
+1 TEXTINCLUDE
+BEGIN
+ "resource.h\0"
+END
+
+2 TEXTINCLUDE
+BEGIN
+ "#define APSTUDIO_HIDDEN_SYMBOLS\r\n"
+ "#include ""windows.h""\r\n"
+ "#undef APSTUDIO_HIDDEN_SYMBOLS\r\n"
+ "\0"
+END
+
+3 TEXTINCLUDE
+BEGIN
+ "#include ""version.rc2""\r\n"
+ "\0"
+END
+
+#endif // APSTUDIO_INVOKED
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// DESIGNINFO
+//
+
+#ifdef APSTUDIO_INVOKED
+GUIDELINES DESIGNINFO
+BEGIN
+ IDD_DLG_SILENT, DIALOG
+ BEGIN
+ BOTTOMMARGIN, 39
+ END
+END
+#endif // APSTUDIO_INVOKED
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// RT_MANIFEST
+//
+//The manifest is commented out because it is no longer needed (it was need for VC6 files)
+//
+//1 RT_MANIFEST "manifest.xml"
+#endif // English (U.S.) resources
+/////////////////////////////////////////////////////////////////////////////
+
+
+
+#ifndef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 3 resource.
+//
+#include "version.rc2"
+
+/////////////////////////////////////////////////////////////////////////////
+#endif // not APSTUDIO_INVOKED
+
diff --git a/Src/Plugins/General/gen_crasher/feedback/feedback.sln b/Src/Plugins/General/gen_crasher/feedback/feedback.sln
new file mode 100644
index 00000000..dad7acb5
--- /dev/null
+++ b/Src/Plugins/General/gen_crasher/feedback/feedback.sln
@@ -0,0 +1,30 @@
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 16
+VisualStudioVersion = 16.0.29424.173
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "feedback", "feedback.vcxproj", "{A845D04C-A95E-424C-BFA8-D7706DBA78BF}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Win32 = Debug|Win32
+ Debug|x64 = Debug|x64
+ Release|Win32 = Release|Win32
+ Release|x64 = Release|x64
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {A845D04C-A95E-424C-BFA8-D7706DBA78BF}.Debug|Win32.ActiveCfg = Debug|Win32
+ {A845D04C-A95E-424C-BFA8-D7706DBA78BF}.Debug|Win32.Build.0 = Debug|Win32
+ {A845D04C-A95E-424C-BFA8-D7706DBA78BF}.Debug|x64.ActiveCfg = Debug|x64
+ {A845D04C-A95E-424C-BFA8-D7706DBA78BF}.Debug|x64.Build.0 = Debug|x64
+ {A845D04C-A95E-424C-BFA8-D7706DBA78BF}.Release|Win32.ActiveCfg = Release|Win32
+ {A845D04C-A95E-424C-BFA8-D7706DBA78BF}.Release|Win32.Build.0 = Release|Win32
+ {A845D04C-A95E-424C-BFA8-D7706DBA78BF}.Release|x64.ActiveCfg = Release|x64
+ {A845D04C-A95E-424C-BFA8-D7706DBA78BF}.Release|x64.Build.0 = Release|x64
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {2C383520-1961-4F68-B42C-3172B6341FB8}
+ EndGlobalSection
+EndGlobal
diff --git a/Src/Plugins/General/gen_crasher/feedback/feedback.vcproj b/Src/Plugins/General/gen_crasher/feedback/feedback.vcproj
new file mode 100644
index 00000000..a8a31b82
--- /dev/null
+++ b/Src/Plugins/General/gen_crasher/feedback/feedback.vcproj
@@ -0,0 +1,321 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioProject
+ ProjectType="Visual C++"
+ Version="9.00"
+ Name="feedback"
+ ProjectGUID="{A845D04C-A95E-424C-BFA8-D7706DBA78BF}"
+ RootNamespace="feedback"
+ Keyword="Win32Proj"
+ TargetFrameworkVersion="131072"
+ >
+ <Platforms>
+ <Platform
+ Name="Win32"
+ />
+ </Platforms>
+ <ToolFiles>
+ </ToolFiles>
+ <Configurations>
+ <Configuration
+ Name="Debug|Win32"
+ OutputDirectory="$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)"
+ ConfigurationType="1"
+ InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC71.vsprops"
+ CharacterSet="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories="."
+ PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_CRT_SECURE_NO_WARNINGS"
+ BasicRuntimeChecks="3"
+ RuntimeLibrary="1"
+ UsePrecompiledHeader="0"
+ PrecompiledHeaderThrough="precomp.h"
+ WarningLevel="3"
+ DebugInformationFormat="4"
+ ForcedIncludeFiles="precomp.h"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="comctl32.lib msvcrtd.lib libcpmtd.lib rpcrt4.lib"
+ OutputFile="$(ProgramFiles)\winamp\reporter.exe"
+ GenerateManifest="false"
+ IgnoreDefaultLibraryNames="libcmtd"
+ GenerateDebugInformation="true"
+ ProgramDatabaseFile="$(OutDir)/feedback.pdb"
+ SubSystem="2"
+ RandomizedBaseAddress="1"
+ DataExecutionPrevention="0"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ AdditionalManifestFiles="manifest.xml"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Release|Win32"
+ OutputDirectory="$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)"
+ ConfigurationType="1"
+ InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC71.vsprops"
+ UseOfATL="0"
+ ATLMinimizesCRunTimeLibraryUsage="false"
+ CharacterSet="1"
+ WholeProgramOptimization="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="1"
+ FavorSizeOrSpeed="2"
+ AdditionalIncludeDirectories="."
+ PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_CRT_SECURE_NO_WARNINGS"
+ StringPooling="true"
+ BufferSecurityCheck="false"
+ UsePrecompiledHeader="2"
+ PrecompiledHeaderThrough="precomp.h"
+ WarningLevel="3"
+ DebugInformationFormat="3"
+ ForcedIncludeFiles="precomp.h"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="comctl32.lib libcpmt.lib rpcrt4.lib"
+ OutputFile="$(ProgramFiles)\winamp\reporter.exe"
+ LinkIncremental="1"
+ GenerateManifest="false"
+ IgnoreDefaultLibraryNames=""
+ GenerateDebugInformation="true"
+ ProgramDatabaseFile="$(OutDir)/$(ProjectName).pdb"
+ SubSystem="2"
+ OptimizeReferences="2"
+ EnableCOMDATFolding="2"
+ EntryPointSymbol=""
+ RandomizedBaseAddress="1"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ AdditionalManifestFiles="manifest.xml"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ </Configurations>
+ <References>
+ </References>
+ <Files>
+ <Filter
+ Name="Source Files"
+ Filter="cpp;c;cxx;def;odl;idl;hpj;bat;asm;asmx"
+ UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"
+ >
+ <File
+ RelativePath=".\main.cpp"
+ >
+ </File>
+ <File
+ RelativePath=".\operations.cpp"
+ >
+ </File>
+ <File
+ RelativePath=".\precomp.cpp"
+ >
+ <FileConfiguration
+ Name="Release|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="1"
+ />
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath=".\silent.cpp"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Header Files"
+ Filter="h;hpp;hxx;hm;inl;inc;xsd"
+ UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"
+ >
+ <File
+ RelativePath=".\main.h"
+ >
+ </File>
+ <File
+ RelativePath=".\precomp.h"
+ >
+ </File>
+ <File
+ RelativePath=".\Resource.h"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Resource Files"
+ Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx"
+ UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}"
+ >
+ <File
+ RelativePath=".\feedback.rc"
+ >
+ </File>
+ <File
+ RelativePath=".\manifest.xml"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Settings"
+ >
+ <File
+ RelativePath="..\config.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\config.h"
+ >
+ </File>
+ <File
+ RelativePath="..\settings.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\settings.h"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="zip"
+ >
+ <File
+ RelativePath=".\xzip\XZip.cpp"
+ >
+ </File>
+ <File
+ RelativePath=".\xzip\XZip.h"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="smtp"
+ >
+ <File
+ RelativePath=".\smtp\Base64.cpp"
+ >
+ </File>
+ <File
+ RelativePath=".\smtp\Base64.h"
+ >
+ </File>
+ <File
+ RelativePath=".\smtp\Smtp.cpp"
+ >
+ </File>
+ <File
+ RelativePath=".\smtp\Smtp.h"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="email"
+ >
+ <File
+ RelativePath=".\email\SendEmail.cpp"
+ >
+ </File>
+ <File
+ RelativePath=".\email\SendEmail.h"
+ >
+ </File>
+ </Filter>
+ </Files>
+ <Globals>
+ </Globals>
+</VisualStudioProject>
diff --git a/Src/Plugins/General/gen_crasher/feedback/feedback.vcxproj b/Src/Plugins/General/gen_crasher/feedback/feedback.vcxproj
new file mode 100644
index 00000000..63bcab20
--- /dev/null
+++ b/Src/Plugins/General/gen_crasher/feedback/feedback.vcxproj
@@ -0,0 +1,253 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Debug|Win32">
+ <Configuration>Debug</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Debug|x64">
+ <Configuration>Debug</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|Win32">
+ <Configuration>Release</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|x64">
+ <Configuration>Release</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <ProjectGuid>{A845D04C-A95E-424C-BFA8-D7706DBA78BF}</ProjectGuid>
+ <RootNamespace>feedback</RootNamespace>
+ <WindowsTargetPlatformVersion>10.0.19041.0</WindowsTargetPlatformVersion>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <PlatformToolset>v142</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <PlatformToolset>v142</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <PlatformToolset>v142</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <PlatformToolset>v142</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>$(PlatformShortName)_$(Configuration)\</OutDir>
+ <IntDir>$(PlatformShortName)_$(Configuration)\</IntDir>
+ <TargetName>reporter</TargetName>
+ <IncludePath>$(IncludePath)</IncludePath>
+ <LibraryPath>$(LibraryPath)</LibraryPath>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>$(PlatformShortName)_$(Configuration)\</OutDir>
+ <IntDir>$(PlatformShortName)_$(Configuration)\</IntDir>
+ <TargetName>reporter</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>$(PlatformShortName)_$(Configuration)\</OutDir>
+ <IntDir>$(PlatformShortName)_$(Configuration)\</IntDir>
+ <TargetName>reporter</TargetName>
+ <IncludePath>$(IncludePath)</IncludePath>
+ <LibraryPath>$(LibraryPath)</LibraryPath>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>$(PlatformShortName)_$(Configuration)\</OutDir>
+ <IntDir>$(PlatformShortName)_$(Configuration)\</IntDir>
+ <TargetName>reporter</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Label="Vcpkg">
+ <VcpkgEnabled>false</VcpkgEnabled>
+ </PropertyGroup>
+ <PropertyGroup Label="Vcpkg" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <VcpkgConfiguration>Debug</VcpkgConfiguration>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <ClCompile>
+ <Optimization>Disabled</Optimization>
+ <AdditionalIncludeDirectories>.;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;_CRT_SECURE_NO_WARNINGS;_WINSOCK_DEPRECATED_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <MinimalRebuild>false</MinimalRebuild>
+ <MultiProcessorCompilation>true</MultiProcessorCompilation>
+ <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
+ <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
+ <WarningLevel>Level3</WarningLevel>
+ <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
+ <DisableSpecificWarnings>4091;4244;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>comctl32.lib;rpcrt4.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <ProgramDatabaseFile>$(IntDir)$(TargetName).pdb</ProgramDatabaseFile>
+ <SubSystem>Windows</SubSystem>
+ <RandomizedBaseAddress>false</RandomizedBaseAddress>
+ <TargetMachine>MachineX86</TargetMachine>
+ <ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>
+ <AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+ </Link>
+ <PostBuildEvent>
+ <Command>xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\
+xcopy /Y /D $(IntDir)$(TargetName).pdb ..\..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\ </Command>
+ <Message>Post build event: 'xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\'</Message>
+ </PostBuildEvent>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <ClCompile>
+ <Optimization>Disabled</Optimization>
+ <AdditionalIncludeDirectories>.;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>WIN64;_DEBUG;_WINDOWS;_CRT_SECURE_NO_WARNINGS;_WINSOCK_DEPRECATED_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <MinimalRebuild>false</MinimalRebuild>
+ <MultiProcessorCompilation>true</MultiProcessorCompilation>
+ <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
+ <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
+ <WarningLevel>Level3</WarningLevel>
+ <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
+ <DisableSpecificWarnings>4091;4244;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>comctl32.lib;rpcrt4.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <ProgramDatabaseFile>$(IntDir)$(TargetName).pdb</ProgramDatabaseFile>
+ <SubSystem>Windows</SubSystem>
+ <RandomizedBaseAddress>false</RandomizedBaseAddress>
+ <ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>
+ <AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+ </Link>
+ <PostBuildEvent>
+ <Command>xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\
+xcopy /Y /D $(IntDir)$(TargetName).pdb ..\..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\ </Command>
+ <Message>Post build event: 'xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\'</Message>
+ </PostBuildEvent>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <ClCompile>
+ <Optimization>MinSpace</Optimization>
+ <FavorSizeOrSpeed>Size</FavorSizeOrSpeed>
+ <AdditionalIncludeDirectories>.;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;_CRT_SECURE_NO_WARNINGS;_WINSOCK_DEPRECATED_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <StringPooling>true</StringPooling>
+ <MultiProcessorCompilation>true</MultiProcessorCompilation>
+ <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+ <BufferSecurityCheck>false</BufferSecurityCheck>
+ <WarningLevel>Level3</WarningLevel>
+ <DebugInformationFormat>None</DebugInformationFormat>
+ <DisableSpecificWarnings>4091;4244;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>comctl32.lib;rpcrt4.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
+ <GenerateDebugInformation>false</GenerateDebugInformation>
+ <ProgramDatabaseFile>$(IntDir)$(TargetName).pdb</ProgramDatabaseFile>
+ <SubSystem>Windows</SubSystem>
+ <OptimizeReferences>true</OptimizeReferences>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <RandomizedBaseAddress>false</RandomizedBaseAddress>
+ <TargetMachine>MachineX86</TargetMachine>
+ <ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>
+ <AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+ </Link>
+ <PostBuildEvent>
+ <Command>xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\ </Command>
+ <Message>Post build event: 'xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\'</Message>
+ </PostBuildEvent>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <ClCompile>
+ <Optimization>MinSpace</Optimization>
+ <FavorSizeOrSpeed>Size</FavorSizeOrSpeed>
+ <AdditionalIncludeDirectories>.;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>WIN64;NDEBUG;_WINDOWS;_CRT_SECURE_NO_WARNINGS;_WINSOCK_DEPRECATED_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <StringPooling>true</StringPooling>
+ <MultiProcessorCompilation>true</MultiProcessorCompilation>
+ <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+ <BufferSecurityCheck>false</BufferSecurityCheck>
+ <WarningLevel>Level3</WarningLevel>
+ <DebugInformationFormat>None</DebugInformationFormat>
+ <DisableSpecificWarnings>4091;4244;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>comctl32.lib;rpcrt4.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
+ <GenerateDebugInformation>false</GenerateDebugInformation>
+ <ProgramDatabaseFile>$(IntDir)$(TargetName).pdb</ProgramDatabaseFile>
+ <SubSystem>Windows</SubSystem>
+ <OptimizeReferences>true</OptimizeReferences>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <RandomizedBaseAddress>false</RandomizedBaseAddress>
+ <ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>
+ <AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+ </Link>
+ <PostBuildEvent>
+ <Command>xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\ </Command>
+ <Message>Post build event: 'xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\'</Message>
+ </PostBuildEvent>
+ </ItemDefinitionGroup>
+ <ItemGroup>
+ <ClCompile Include="..\config.cpp" />
+ <ClCompile Include="..\settings.cpp" />
+ <ClCompile Include="email\SendEmail.cpp" />
+ <ClCompile Include="main.cpp" />
+ <ClCompile Include="operations.cpp" />
+ <ClCompile Include="silent.cpp" />
+ <ClCompile Include="smtp\Base64.cpp" />
+ <ClCompile Include="smtp\Smtp.cpp" />
+ <ClCompile Include="xzip\XZip.cpp" />
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="..\config.h" />
+ <ClInclude Include="..\settings.h" />
+ <ClInclude Include="email\SendEmail.h" />
+ <ClInclude Include="main.h" />
+ <ClInclude Include="Resource.h" />
+ <ClInclude Include="smtp\Base64.h" />
+ <ClInclude Include="smtp\Smtp.h" />
+ <ClInclude Include="xzip\XZip.h" />
+ </ItemGroup>
+ <ItemGroup>
+ <ResourceCompile Include="feedback.rc" />
+ </ItemGroup>
+ <ItemGroup>
+ <Xml Include="manifest.xml" />
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project> \ No newline at end of file
diff --git a/Src/Plugins/General/gen_crasher/feedback/feedback.vcxproj.filters b/Src/Plugins/General/gen_crasher/feedback/feedback.vcxproj.filters
new file mode 100644
index 00000000..f2609acf
--- /dev/null
+++ b/Src/Plugins/General/gen_crasher/feedback/feedback.vcxproj.filters
@@ -0,0 +1,79 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup>
+ <ClCompile Include="smtp\Base64.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\config.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="main.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="operations.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="email\SendEmail.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\settings.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="silent.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="smtp\Smtp.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="xzip\XZip.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="smtp\Base64.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\config.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="main.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="Resource.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="email\SendEmail.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\settings.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="smtp\Smtp.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="xzip\XZip.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ </ItemGroup>
+ <ItemGroup>
+ <Filter Include="Header Files">
+ <UniqueIdentifier>{de803cce-7a8d-46da-97a8-cb794b2ef154}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Ressource Files">
+ <UniqueIdentifier>{6cea30eb-0534-44ec-af7c-340d8047d962}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Source Files">
+ <UniqueIdentifier>{a281ea66-f75c-47a9-90b7-61a031b4ea22}</UniqueIdentifier>
+ </Filter>
+ </ItemGroup>
+ <ItemGroup>
+ <ResourceCompile Include="feedback.rc">
+ <Filter>Ressource Files</Filter>
+ </ResourceCompile>
+ </ItemGroup>
+ <ItemGroup>
+ <Xml Include="manifest.xml">
+ <Filter>Ressource Files</Filter>
+ </Xml>
+ </ItemGroup>
+</Project> \ No newline at end of file
diff --git a/Src/Plugins/General/gen_crasher/feedback/main.cpp b/Src/Plugins/General/gen_crasher/feedback/main.cpp
new file mode 100644
index 00000000..b425fd15
--- /dev/null
+++ b/Src/Plugins/General/gen_crasher/feedback/main.cpp
@@ -0,0 +1,79 @@
+// feedback.cpp : Defines the entry point for the application.
+//
+#include ".\main.h"
+#include <commctrl.h>
+#include <strsafe.h>
+Settings settings;
+
+int WINAPI wWinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpszCmdLine, int nCmdShow)
+{
+ wchar_t *argv[2] = {0};
+ int argc = 0;
+ if (lpszCmdLine && wcslen(lpszCmdLine) >0) argc = ParseCommandLine(lpszCmdLine, NULL);
+ if (argc != 1)
+ {
+ MSGBOXPARAMSW msgbx = {sizeof(MSGBOXPARAMS),0};
+ msgbx.lpszText = L"Winamp Error Reporter\nCopyright © 2005-2014 Winamp SA";
+ msgbx.lpszCaption = L"About...";
+ msgbx.lpszIcon = MAKEINTRESOURCEW(ICON_XP);
+ msgbx.hInstance = GetModuleHandle(0);
+ msgbx.dwStyle = MB_USERICON;
+ MessageBoxIndirectW(&msgbx);
+ return 0;
+ }
+
+ ParseCommandLine(lpszCmdLine, argv);
+ settings.SetPath(argv[0]);
+
+ if (!settings.Load())
+ {
+ MessageBox(NULL, L"Unable to load settings.", L"Error", MB_OK);
+ return 0;
+ }
+
+ INITCOMMONCONTROLSEX icex = {sizeof(icex), ICC_WIN95_CLASSES};
+ InitCommonControlsEx(&icex);
+ DialogBoxParam(hInstance, MAKEINTRESOURCE(IDD_DLG_SILENT), NULL, (DLGPROC)SilentDlgProc, (LPARAM)hInstance);
+ return 0;
+}
+
+static int ParseCommandLine(wchar_t *cmdline, wchar_t **argv)
+{
+ wchar_t *bufp;
+ int argc;
+ argc = 0;
+ for ( bufp = cmdline; *bufp; )
+ {
+ /* Skip leading whitespace */
+ while ( isspace(*bufp) ) ++bufp;
+ /* Skip over argument */
+ if ( *bufp == L'"' )
+ {
+ ++bufp;
+ if ( *bufp )
+ {
+ if ( argv ) argv[argc] = bufp;
+ ++argc;
+ }
+ /* Skip over word */
+ while ( *bufp && (*bufp != L'"') ) ++bufp;
+ }
+ else
+ {
+ if ( *bufp )
+ {
+ if ( argv ) argv[argc] = bufp;
+ ++argc;
+ }
+ /* Skip over word */
+ while ( *bufp && ! isspace(*bufp) ) ++bufp;
+ }
+ if ( *bufp )
+ {
+ if ( argv ) *bufp = L'\0';
+ ++bufp;
+ }
+ }
+ if ( argv ) argv[argc] = NULL;
+ return(argc);
+} \ No newline at end of file
diff --git a/Src/Plugins/General/gen_crasher/feedback/main.h b/Src/Plugins/General/gen_crasher/feedback/main.h
new file mode 100644
index 00000000..bd2363fd
--- /dev/null
+++ b/Src/Plugins/General/gen_crasher/feedback/main.h
@@ -0,0 +1,19 @@
+#ifndef FEEDBACK_H
+#define FEEDBACK_H
+
+#include <windows.h>
+#include "resource.h"
+
+#include "..\settings.h"
+
+extern Settings settings;
+
+static int ParseCommandLine(wchar_t *cmdline, wchar_t **argv);
+
+BOOL CALLBACK SilentDlgProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
+
+BOOL ZipData(void);
+BOOL SendData(HWND hwnd);
+BOOL Restart(void);
+
+#endif FEEDBACK_H \ No newline at end of file
diff --git a/Src/Plugins/General/gen_crasher/feedback/manifest.xml b/Src/Plugins/General/gen_crasher/feedback/manifest.xml
new file mode 100644
index 00000000..651ac743
--- /dev/null
+++ b/Src/Plugins/General/gen_crasher/feedback/manifest.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0" xmlns:asmv3="urn:schemas-microsoft-com:asm.v3" >
+ <assemblyIdentity
+ version="1.11.0.0"
+ processorArchitecture="X86"
+ name="Nullsoft.Error Reporter"
+ type="win32"
+ />
+ <description>Nullsoft Error Reporter</description>
+ <dependency>
+ <dependentAssembly>
+ <assemblyIdentity
+ type="win32"
+ name="Microsoft.Windows.Common-Controls"
+ version="6.0.0.0"
+ processorArchitecture="X86"
+ publicKeyToken="6595b64144ccf1df"
+ language="*"
+ />
+ </dependentAssembly>
+ </dependency>
+ <trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">
+ <security>
+ <requestedPrivileges>
+ <requestedExecutionLevel
+ level="asInvoker"
+ />
+ </requestedPrivileges>
+ </security>
+ </trustInfo>
+ <asmv3:application>
+ <asmv3:windowsSettings xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">
+ <dpiAware>true</dpiAware>
+ </asmv3:windowsSettings>
+ </asmv3:application>
+</assembly> \ No newline at end of file
diff --git a/Src/Plugins/General/gen_crasher/feedback/operations.cpp b/Src/Plugins/General/gen_crasher/feedback/operations.cpp
new file mode 100644
index 00000000..5919c690
--- /dev/null
+++ b/Src/Plugins/General/gen_crasher/feedback/operations.cpp
@@ -0,0 +1,163 @@
+#include ".\main.h"
+#include ".\xzip\xzip.h"
+#include ".\smtp\smtp.h"
+#include ".\email\sendemail.h"
+#include <strsafe.h>
+
+const wchar_t* GetFileName(const wchar_t *fullname)
+{
+ if (!fullname) return NULL;
+ const wchar_t *start = wcsrchr(fullname, L'\\');
+ if (start && start != fullname) start = CharNext(start);
+
+ return start;
+}
+
+BOOL ZipData(void)
+{
+ BOOL retCode = FALSE;
+ HZIP hz = CreateZip(settings.zipPath, 0, ZIP_FILENAME);
+ if (hz)
+ {
+ retCode = TRUE;
+ if (settings.createLOG && settings.ReadLogCollectResult()) retCode = (ZR_OK == ZipAdd(hz, GetFileName(settings.logPath), settings.logPath, 0, ZIP_FILENAME));
+ if (retCode && settings.createDMP && settings.ReadDmpCollectResult()) retCode = (ZR_OK == ZipAdd(hz, GetFileName(settings.dumpPath), settings.dumpPath, 0, ZIP_FILENAME));
+ }
+ CloseZip(hz);
+ return retCode;
+}
+
+LPCTSTR BuildSubjectString(LPCTSTR subject)
+{
+ static wchar_t subject_str[512] = {L"Winamp Error Report"};
+ wchar_t uid_str[64] = {0}, path[MAX_PATH] = {0};
+ if (GetModuleFileName(0, path, MAX_PATH))
+ {
+ PathRemoveFileSpec(path);
+ wchar_t *p = path + wcslen(path) - 1;
+ while(p && *p && *p != L'\\')
+ {
+ p = CharPrev(path, p);
+ }
+ if (p) *p = 0;
+ PathAppend(path, L"winamp.exe");
+
+ HKEY hkey = NULL;
+ if (RegCreateKeyEx(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Nullsoft\\Winamp", 0, 0, 0, KEY_READ, NULL, &hkey, NULL) == ERROR_SUCCESS)
+ {
+ DWORD s = 512, t = REG_SZ;
+ if (RegQueryValueEx(hkey, path, 0, &t, (LPBYTE)uid_str, &s) != ERROR_SUCCESS || t != REG_SZ) uid_str[0] = 0;
+ RegCloseKey(hkey);
+ }
+ }
+
+ // if it fails then we'll need to make something...
+ if (!uid_str[0])
+ {
+ GUID guid;
+ UuidCreate(&guid);
+ StringCbPrintf(uid_str, ARRAYSIZE(uid_str), L"%08X%04X%04X%02X%02X%02X%02X%02X%02X%02X%02X",
+ (int)guid.Data1, (int)guid.Data2, (int)guid.Data3, (int)guid.Data4[0],
+ (int)guid.Data4[1], (int)guid.Data4[2], (int)guid.Data4[3],
+ (int)guid.Data4[4], (int)guid.Data4[5], (int)guid.Data4[6], (int)guid.Data4[7]);
+ }
+
+ if (StringCchPrintfW(subject_str, ARRAYSIZE(subject_str), L"%s [%s]", subject, uid_str) == S_OK)
+ return subject_str;
+ else
+ return subject;
+}
+
+BOOL SendData(HWND hwnd)
+{
+ BOOL retCode = FALSE;
+ const wchar_t *subject = L"Winamp Error Report";
+ const wchar_t *senderName = L"Winamp Error Reporter";
+ const wchar_t *recipientAddress = L"bug@winamp.com";
+ const wchar_t *recipientName = L"Nullsoft Bug Reporting";
+ wchar_t *msgInfo = _wcsdup(settings.ReadBody());
+
+ wchar_t *p = msgInfo, *end = p + wcslen(msgInfo);
+ while(p != end)
+ {
+ if (*p == 1) *p = L'\r';
+ if (*p == 2) *p = L'\n';
+ p++;
+ }
+
+ if (settings.sendBySMTP)
+ {
+ CSmtp smtp;
+ CSmtpMessage msg;
+ CSmtpMessageBody body;
+ CSmtpAttachment attach;
+
+ msg.Subject = BuildSubjectString(subject);
+
+ // Who the message is from
+ msg.Sender.Name = senderName;
+ msg.Sender.Address = settings.smtpAddress;
+ msg.Recipient.Address = recipientAddress;
+ msg.Recipient.Name = recipientName;
+ if(settings.zipData )
+ {
+ attach.FileName = settings.zipPath;
+ msg.Attachments.Add(attach);
+ }
+ else
+ {
+ if (settings.createLOG && settings.ReadLogCollectResult()) attach.FileName = settings.logPath;
+ msg.Attachments.Add(attach);
+ if (settings.createDMP && settings.ReadDmpCollectResult()) attach.FileName = settings.dumpPath;
+ msg.Attachments.Add(attach);
+ }
+
+ // smtp.m_wSmtpPort = settings.smtpPort; - not working for some reasons
+ if (settings.smtpAuth)
+ {
+ smtp.m_strUser = settings.smtpUser;
+ smtp.m_strPass = settings.smtpPwd;;
+ }
+
+ body = L"This message was generated by Winamp Error Reporter v1.09.\r\nPlease check attachments for viruses.\r\n";
+ body.Data.append(L"\r\n");
+ body.Data.append(msgInfo);
+
+ msg.Message.Add(body);
+ retCode = smtp.Connect(settings.smtpServer);
+ if ( retCode )
+ {
+ // Send the message and close the connection afterwards
+ retCode = (smtp.SendMessage(msg) == 0);
+ smtp.Close();
+ }
+ }
+ else if(settings.sendByClient)
+ {
+ retCode = SendEmail(hwnd, recipientAddress, recipientName, BuildSubjectString(subject), msgInfo, settings.zipPath);
+ }
+
+ return retCode;
+}
+
+BOOL Restart(void)
+{
+ STARTUPINFO si = {0};
+ si.cb = sizeof(si);
+ si.dwFlags = STARTF_USESHOWWINDOW;
+ si.wShowWindow = SW_SHOW;
+
+ PROCESS_INFORMATION pi = {0};
+ return CreateProcess(
+ settings.ReadWinamp(), // name of executable module
+ NULL, // command line string
+ NULL, // process attributes
+ NULL, // thread attributes
+ FALSE, // handle inheritance option
+ 0, // creation flags
+ NULL, // new environment block
+ NULL, // current directory name
+ &si, // startup information
+ &pi // process information
+ );
+} \ No newline at end of file
diff --git a/Src/Plugins/General/gen_crasher/feedback/resource.h b/Src/Plugins/General/gen_crasher/feedback/resource.h
new file mode 100644
index 00000000..4c564828
--- /dev/null
+++ b/Src/Plugins/General/gen_crasher/feedback/resource.h
@@ -0,0 +1,29 @@
+//{{NO_DEPENDENCIES}}
+// Microsoft Visual C++ generated include file.
+// Used by feedback.rc
+//
+#define IDC_MYICON 2
+#define IDD_DLG_SILENT 102
+#define IDS_APP_TITLE 103
+#define IDM_ABOUT 104
+#define IDM_EXIT 105
+#define ICON_XP 108
+#define IDR_MAINFRAME 128
+#define IDR_RT_MANIFEST1 133
+#define IDC_PRG_COLLECT 1000
+#define IDC_LBL_STEP 1001
+#define IDC_BUTTON1 1003
+#define IDC_BUTTON2 1004
+#define IDC_STATIC -1
+
+// Next default values for new objects
+//
+#ifdef APSTUDIO_INVOKED
+#ifndef APSTUDIO_READONLY_SYMBOLS
+#define _APS_NO_MFC 1
+#define _APS_NEXT_RESOURCE_VALUE 134
+#define _APS_NEXT_COMMAND_VALUE 32771
+#define _APS_NEXT_CONTROL_VALUE 1005
+#define _APS_NEXT_SYMED_VALUE 110
+#endif
+#endif
diff --git a/Src/Plugins/General/gen_crasher/feedback/silent.cpp b/Src/Plugins/General/gen_crasher/feedback/silent.cpp
new file mode 100644
index 00000000..3d756e70
--- /dev/null
+++ b/Src/Plugins/General/gen_crasher/feedback/silent.cpp
@@ -0,0 +1,134 @@
+#include ".\main.h"
+#include <commctrl.h>
+#include <shlobj.h>
+#include "resource.h"
+
+BOOL CALLBACK SilentDlgProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+ switch(uMsg)
+ {
+ case WM_INITDIALOG:
+ {
+ HICON hIcon = LoadIcon(GetModuleHandle(NULL), MAKEINTRESOURCE(ICON_XP));
+ SetClassLongPtr(hwndDlg, GCLP_HICON, (LONG_PTR)hIcon);
+
+ HWND hwndPrg = GetDlgItem(hwndDlg, IDC_PRG_COLLECT);
+ SendMessage(hwndPrg, PBM_SETRANGE, 0, MAKELPARAM(0,100));
+ SendMessage(hwndPrg, PBM_SETPOS, 0, 0);
+ SetDlgItemText(hwndDlg, IDC_LBL_STEP, L"Starting reporter...");
+ ShowWindow(GetDlgItem(hwndDlg, IDC_BUTTON1), SW_HIDE);
+ ShowWindow(GetDlgItem(hwndDlg, IDC_BUTTON2), SW_HIDE);
+ UpdateWindow(hwndDlg);
+
+ if ((settings.createLOG && !settings.ReadLogCollectResult()) &&
+ (settings.createDMP && !settings.ReadDmpCollectResult()) )
+ {
+ SetDlgItemText(hwndDlg, IDC_LBL_STEP, L"Error. Data was not generated.");
+ SendMessage(hwndPrg, PBM_SETPOS, 100, 0);
+ UpdateWindow(hwndDlg);
+ SetTimer(hwndDlg, 126, 2000, NULL);
+ break;
+ }
+ SetTimer(hwndDlg, 123, 500, NULL);
+ break;
+ }
+ case WM_COMMAND:
+ switch(LOWORD(wParam))
+ {
+ case IDC_BUTTON1:
+ {
+ BOOL ret = FALSE;
+ wchar_t file[MAX_PATH] = {0};
+ lstrcpyn(file, settings.zipPath, MAX_PATH);
+
+ LPSHELLFOLDER pDesktopFolder = 0;
+ if(SUCCEEDED(SHGetDesktopFolder(&pDesktopFolder)))
+ {
+ LPITEMIDLIST filepidl = 0;
+ HRESULT hr = pDesktopFolder->ParseDisplayName(NULL,0,file,0,&filepidl,0);
+ if(FAILED(hr)){ pDesktopFolder->Release(); ret = FALSE; }
+ else
+ {
+ if(SUCCEEDED(SHOpenFolderAndSelectItems(filepidl,0,NULL,NULL))){
+ ret = TRUE;
+ }
+ }
+ }
+
+ if (ret == FALSE)
+ {
+ SetDlgItemText(hwndDlg, IDC_LBL_STEP, L"Error. Unable to locate crash report.");
+ UpdateWindow(hwndDlg);
+ }
+ }
+ break;
+ case IDCANCEL:
+ case IDC_BUTTON2:
+ SetTimer(hwndDlg, 126, 1, NULL);
+ break;
+ }
+ break;
+ case WM_TIMER:
+ if (wParam == 123)
+ {
+ KillTimer(hwndDlg, wParam);
+ HWND hwndPrg;
+ hwndPrg = GetDlgItem(hwndDlg, IDC_PRG_COLLECT);
+ SendMessage(hwndPrg, PBM_SETPOS, 20, 0);
+ if (settings.zipData)
+ {
+ SetDlgItemText(hwndDlg, IDC_LBL_STEP, L"Packing results...");
+ if(!ZipData())
+ {
+ SetDlgItemText(hwndDlg, IDC_LBL_STEP, L"Error. Unable to pack results.");
+ SendMessage(hwndPrg, PBM_SETPOS, 100, 0);
+ UpdateWindow(hwndDlg);
+ SetTimer(hwndDlg, 126, 2000, NULL);
+ break;
+ }
+ }
+ SendMessage(hwndPrg, PBM_SETPOS, 40, 0);
+ UpdateWindow(hwndDlg);
+ if (settings.sendData)
+ {
+ SetDlgItemText(hwndDlg, IDC_LBL_STEP, L"Sending results...");
+ UpdateWindow(hwndDlg);
+ if(!SendData(hwndDlg))
+ {
+ SetDlgItemText(hwndDlg, IDC_LBL_STEP, L"Error. Unable to send crash report.");
+ SendMessage(hwndPrg, PBM_SETPOS, 100, 0);
+ ShowWindow(GetDlgItem(hwndDlg, IDC_BUTTON1), SW_SHOW);
+ ShowWindow(GetDlgItem(hwndDlg, IDC_BUTTON2), SW_SHOW);
+ ShowWindow(GetDlgItem(hwndDlg, IDC_PRG_COLLECT), SW_HIDE);
+ UpdateWindow(hwndDlg);
+ break;
+ }
+ }
+ if (settings.autoRestart)
+ {
+ SetDlgItemText(hwndDlg, IDC_LBL_STEP, L"Restarting Winamp...");
+ SendMessage(hwndPrg, PBM_SETPOS, 80, 0);
+ UpdateWindow(hwndDlg);
+ if(!Restart())
+ {
+ SetDlgItemText(hwndDlg, IDC_LBL_STEP, L"Error. Unable to restart Winamp.");
+ SendMessage(hwndPrg, PBM_SETPOS, 100, 0);
+ UpdateWindow(hwndDlg);
+ SetTimer(hwndDlg, 126, 2000, NULL);
+ break;
+ }
+ }
+ SetDlgItemText(hwndDlg, IDC_LBL_STEP, L"Done.");
+ SendMessage(hwndPrg, PBM_SETPOS, 100, 0);
+ UpdateWindow(hwndDlg);
+ SetTimer(hwndDlg, 126, 1000, NULL);
+ }
+ else if (wParam == 126)
+ {
+ KillTimer(hwndDlg, wParam);
+ EndDialog(hwndDlg, TRUE);
+ }
+ break;
+ }
+ return FALSE;
+} \ No newline at end of file
diff --git a/Src/Plugins/General/gen_crasher/feedback/smtp/Base64.cpp b/Src/Plugins/General/gen_crasher/feedback/smtp/Base64.cpp
new file mode 100644
index 00000000..fb95336e
--- /dev/null
+++ b/Src/Plugins/General/gen_crasher/feedback/smtp/Base64.cpp
@@ -0,0 +1,320 @@
+// CBase64.cpp: implementation of the CBase64 class.
+//
+//////////////////////////////////////////////////////////////////////
+
+#include "Base64.h"
+
+// Digits...
+static char Base64Digits[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+BOOL CBase64::m_Init = FALSE;
+char CBase64::m_DecodeTable[256];
+
+#ifndef PAGESIZE
+#define PAGESIZE 4096
+#endif
+
+#ifndef ROUNDTOPAGE
+#define ROUNDTOPAGE(a) (((a/4096)+1)*4096)
+#endif
+
+//////////////////////////////////////////////////////////////////////
+// Construction/Destruction
+//////////////////////////////////////////////////////////////////////
+
+CBase64::CBase64()
+ : m_pDBuffer(NULL),
+ m_pEBuffer(NULL),
+ m_nDBufLen(0),
+ m_nEBufLen(0),
+ m_nDDataLen(0),
+ m_nEDataLen(0)
+{
+
+}
+
+CBase64::~CBase64()
+{
+ if(m_pDBuffer != NULL)
+ delete [] m_pDBuffer;
+
+ if(m_pEBuffer != NULL)
+ delete [] m_pEBuffer;
+}
+
+LPCSTR CBase64::DecodedMessage() const
+{
+ return (LPCSTR) m_pDBuffer;
+}
+
+LPCSTR CBase64::EncodedMessage() const
+{
+ return (LPCSTR) m_pEBuffer;
+}
+
+void CBase64::AllocEncode(DWORD nSize)
+{
+ if(m_nEBufLen < nSize)
+ {
+ if(m_pEBuffer != NULL)
+ delete [] m_pEBuffer;
+
+ m_nEBufLen = ROUNDTOPAGE(nSize);
+ m_pEBuffer = new BYTE[m_nEBufLen];
+ }
+
+ ::ZeroMemory(m_pEBuffer, m_nEBufLen);
+ m_nEDataLen = 0;
+}
+
+void CBase64::AllocDecode(DWORD nSize)
+{
+ if(m_nDBufLen < nSize)
+ {
+ if(m_pDBuffer != NULL)
+ delete [] m_pDBuffer;
+
+ m_nDBufLen = ROUNDTOPAGE(nSize);
+ m_pDBuffer = new BYTE[m_nDBufLen];
+ }
+
+ ::ZeroMemory(m_pDBuffer, m_nDBufLen);
+ m_nDDataLen = 0;
+}
+
+void CBase64::SetEncodeBuffer(const PBYTE pBuffer, DWORD nBufLen)
+{
+ DWORD i = 0;
+
+ AllocEncode(nBufLen);
+ while(i < nBufLen)
+ {
+ if(!_IsBadMimeChar(pBuffer[i]))
+ {
+ m_pEBuffer[m_nEDataLen] = pBuffer[i];
+ m_nEDataLen++;
+ }
+
+ i++;
+ }
+}
+
+void CBase64::SetDecodeBuffer(const PBYTE pBuffer, DWORD nBufLen)
+{
+ AllocDecode(nBufLen);
+ ::CopyMemory(m_pDBuffer, pBuffer, nBufLen);
+ m_nDDataLen = nBufLen;
+}
+
+void CBase64::Encode(const PBYTE pBuffer, DWORD nBufLen)
+{
+ SetDecodeBuffer(pBuffer, nBufLen);
+ AllocEncode(nBufLen * 2);
+
+ TempBucket Raw;
+ DWORD nIndex = 0;
+
+ while((nIndex + 3) <= nBufLen)
+ {
+ Raw.Clear();
+ ::CopyMemory(&Raw, m_pDBuffer + nIndex, 3);
+ Raw.nSize = 3;
+ _EncodeToBuffer(Raw, m_pEBuffer + m_nEDataLen);
+ nIndex += 3;
+ m_nEDataLen += 4;
+ }
+
+ if(nBufLen > nIndex)
+ {
+ Raw.Clear();
+ Raw.nSize = (BYTE) (nBufLen - nIndex);
+ ::CopyMemory(&Raw, m_pDBuffer + nIndex, nBufLen - nIndex);
+ _EncodeToBuffer(Raw, m_pEBuffer + m_nEDataLen);
+ m_nEDataLen += 4;
+ }
+}
+
+void CBase64::Encode(LPCSTR szMessage)
+{
+ if(szMessage != NULL)
+ CBase64::Encode((const PBYTE)szMessage, lstrlenA(szMessage));
+}
+
+void CBase64::Decode(const PBYTE pBuffer, DWORD dwBufLen)
+{
+ if(!CBase64::m_Init)
+ _Init();
+
+ SetEncodeBuffer(pBuffer, dwBufLen);
+
+ AllocDecode(dwBufLen);
+
+ TempBucket Raw;
+
+ DWORD nIndex = 0;
+
+ while((nIndex + 4) <= m_nEDataLen)
+ {
+ Raw.Clear();
+ Raw.nData[0] = CBase64::m_DecodeTable[m_pEBuffer[nIndex]];
+ Raw.nData[1] = CBase64::m_DecodeTable[m_pEBuffer[nIndex + 1]];
+ Raw.nData[2] = CBase64::m_DecodeTable[m_pEBuffer[nIndex + 2]];
+ Raw.nData[3] = CBase64::m_DecodeTable[m_pEBuffer[nIndex + 3]];
+
+ if(Raw.nData[2] == 255)
+ Raw.nData[2] = 0;
+ if(Raw.nData[3] == 255)
+ Raw.nData[3] = 0;
+
+ Raw.nSize = 4;
+ _DecodeToBuffer(Raw, m_pDBuffer + m_nDDataLen);
+ nIndex += 4;
+ m_nDDataLen += 3;
+ }
+
+ // If nIndex < m_nEDataLen, then we got a decode message without padding.
+ // We may want to throw some kind of warning here, but we are still required
+ // to handle the decoding as if it was properly padded.
+ if(nIndex < m_nEDataLen)
+ {
+ Raw.Clear();
+ for(DWORD i = nIndex; i < m_nEDataLen; i++)
+ {
+ Raw.nData[i - nIndex] = CBase64::m_DecodeTable[m_pEBuffer[i]];
+ Raw.nSize++;
+ if(Raw.nData[i - nIndex] == 255)
+ Raw.nData[i - nIndex] = 0;
+ }
+
+ _DecodeToBuffer(Raw, m_pDBuffer + m_nDDataLen);
+ m_nDDataLen += (m_nEDataLen - nIndex);
+ }
+}
+
+void CBase64::Decode(LPCSTR szMessage)
+{
+ if(szMessage != NULL)
+ CBase64::Decode((const PBYTE)szMessage, lstrlenA(szMessage));
+}
+
+DWORD CBase64::_DecodeToBuffer(const TempBucket &Decode, PBYTE pBuffer)
+{
+ TempBucket Data;
+ DWORD nCount = 0;
+
+ _DecodeRaw(Data, Decode);
+
+ for(int i = 0; i < 3; i++)
+ {
+ pBuffer[i] = Data.nData[i];
+ if(pBuffer[i] != 255)
+ nCount++;
+ }
+
+ return nCount;
+}
+
+
+void CBase64::_EncodeToBuffer(const TempBucket &Decode, PBYTE pBuffer)
+{
+ TempBucket Data;
+
+ _EncodeRaw(Data, Decode);
+
+ for(int i = 0; i < 4; i++)
+ pBuffer[i] = Base64Digits[Data.nData[i]];
+
+ switch(Decode.nSize)
+ {
+ case 1:
+ pBuffer[2] = '=';
+ case 2:
+ pBuffer[3] = '=';
+ }
+}
+
+void CBase64::_DecodeRaw(TempBucket &Data, const TempBucket &Decode)
+{
+ BYTE nTemp;
+
+ Data.nData[0] = Decode.nData[0];
+ Data.nData[0] <<= 2;
+
+ nTemp = Decode.nData[1];
+ nTemp >>= 4;
+ nTemp &= 0x03;
+ Data.nData[0] |= nTemp;
+
+ Data.nData[1] = Decode.nData[1];
+ Data.nData[1] <<= 4;
+
+ nTemp = Decode.nData[2];
+ nTemp >>= 2;
+ nTemp &= 0x0F;
+ Data.nData[1] |= nTemp;
+
+ Data.nData[2] = Decode.nData[2];
+ Data.nData[2] <<= 6;
+ nTemp = Decode.nData[3];
+ nTemp &= 0x3F;
+ Data.nData[2] |= nTemp;
+}
+
+void CBase64::_EncodeRaw(TempBucket &Data, const TempBucket &Decode)
+{
+ BYTE nTemp;
+
+ Data.nData[0] = Decode.nData[0];
+ Data.nData[0] >>= 2;
+
+ Data.nData[1] = Decode.nData[0];
+ Data.nData[1] <<= 4;
+ nTemp = Decode.nData[1];
+ nTemp >>= 4;
+ Data.nData[1] |= nTemp;
+ Data.nData[1] &= 0x3F;
+
+ Data.nData[2] = Decode.nData[1];
+ Data.nData[2] <<= 2;
+
+ nTemp = Decode.nData[2];
+ nTemp >>= 6;
+
+ Data.nData[2] |= nTemp;
+ Data.nData[2] &= 0x3F;
+
+ Data.nData[3] = Decode.nData[2];
+ Data.nData[3] &= 0x3F;
+}
+
+BOOL CBase64::_IsBadMimeChar(BYTE nData)
+{
+ switch(nData)
+ {
+ case '\r': case '\n': case '\t': case ' ' :
+ case '\b': case '\a': case '\f': case '\v':
+ return TRUE;
+ default:
+ return FALSE;
+ }
+}
+
+void CBase64::_Init()
+{ // Initialize Decoding table.
+
+ int i;
+
+ for(i = 0; i < 256; i++)
+ CBase64::m_DecodeTable[i] = -2;
+
+ for(i = 0; i < 64; i++)
+ {
+ CBase64::m_DecodeTable[Base64Digits[i]] = (CHAR)i;
+ CBase64::m_DecodeTable[Base64Digits[i]|0x80] = (CHAR)i;
+ }
+
+ CBase64::m_DecodeTable['='] = -1;
+ CBase64::m_DecodeTable['='|0x80] = -1;
+
+ CBase64::m_Init = TRUE;
+}
diff --git a/Src/Plugins/General/gen_crasher/feedback/smtp/Base64.h b/Src/Plugins/General/gen_crasher/feedback/smtp/Base64.h
new file mode 100644
index 00000000..4e5d3924
--- /dev/null
+++ b/Src/Plugins/General/gen_crasher/feedback/smtp/Base64.h
@@ -0,0 +1,63 @@
+// CBase64.h: interface for the CBase64 class.
+//
+//////////////////////////////////////////////////////////////////////
+
+#if !defined(AFX_CBase64_H__B2E45717_0625_11D2_A80A_00C04FB6794C__INCLUDED_)
+#define AFX_CBase64_H__B2E45717_0625_11D2_A80A_00C04FB6794C__INCLUDED_
+
+#include <windows.h>
+
+#define lCONTEXT char
+#define PlCONTEXT lCONTEXT*
+
+
+
+class CBase64
+{
+ // Internal bucket class.
+ class TempBucket
+ {
+ public:
+ BYTE nData[4];
+ BYTE nSize;
+ void Clear() { ::ZeroMemory(nData, 4); nSize = 0; };
+ };
+
+ PBYTE m_pDBuffer;
+ PBYTE m_pEBuffer;
+ DWORD m_nDBufLen;
+ DWORD m_nEBufLen;
+ DWORD m_nDDataLen;
+ DWORD m_nEDataLen;
+
+public:
+ CBase64();
+ virtual ~CBase64();
+
+public:
+ virtual void Encode(const PBYTE, DWORD);
+ virtual void Decode(const PBYTE, DWORD);
+ virtual void Encode(LPCSTR sMessage);
+ virtual void Decode(LPCSTR sMessage);
+
+ virtual LPCSTR DecodedMessage() const;
+ virtual LPCSTR EncodedMessage() const;
+
+ virtual void AllocEncode(DWORD);
+ virtual void AllocDecode(DWORD);
+ virtual void SetEncodeBuffer(const PBYTE pBuffer, DWORD nBufLen);
+ virtual void SetDecodeBuffer(const PBYTE pBuffer, DWORD nBufLen);
+
+protected:
+ virtual void _EncodeToBuffer(const TempBucket &Decode, PBYTE pBuffer);
+ virtual ULONG _DecodeToBuffer(const TempBucket &Decode, PBYTE pBuffer);
+ virtual void _EncodeRaw(TempBucket &, const TempBucket &);
+ virtual void _DecodeRaw(TempBucket &, const TempBucket &);
+ virtual BOOL _IsBadMimeChar(BYTE);
+
+ static char m_DecodeTable[256];
+ static BOOL m_Init;
+ void _Init();
+};
+
+#endif // !defined(AFX_CBase64_H__B2E45717_0625_11D2_A80A_00C04FB6794C__INCLUDED_)
diff --git a/Src/Plugins/General/gen_crasher/feedback/smtp/Smtp.cpp b/Src/Plugins/General/gen_crasher/feedback/smtp/Smtp.cpp
new file mode 100644
index 00000000..97e319e1
--- /dev/null
+++ b/Src/Plugins/General/gen_crasher/feedback/smtp/Smtp.cpp
@@ -0,0 +1,1468 @@
+//////////////////////////////////////////////////////////////////////
+/*
+ Smtp.cpp: implementation of the CSmtp and CSmtpMessage classes
+
+ Written by Robert Simpson (robert@blackcastlesoft.com)
+ Created 11/1/2000
+ Version 1.7 -- Last Modified 06/18/2001
+
+ 1.7 - Modified the code that gets the GMT offset and the code that
+ parses the date/time as per Noa Karsten's suggestions on
+ codeguru.
+ - Added an FD_ZERO(&set) to the last part of SendCmd(), since
+ I use the set twice and only zero it out once. Submitted by
+ Marc Allen.
+ - Removed the requirement that a message have a body and/or an
+ attachment. This allows for sending messages with only a
+ subject line. Submitted by Marc Allen.
+ 1.6 - Apparently older versions of the STL do not have the clear()
+ method for basic_string's. I modified the code to use
+ erase() instead.
+ - Added #include <atlbase.h> to the smtp.h file, which will
+ allow any app to use these classes without problems.
+ 1.5 - Guess I should have checked EncodeQuotedPrintable() as well,
+ since it did the same thing BreakMessage() did in adding an
+ extranneous CRLF to the end of any text it processed. Fixed.
+ 1.4 - BreakMesage() added an extranneous CRLF to the end of any
+ text it processed, which is now fixed. Certainly not a big
+ deal, but it caused text attachments to not be 100% identical
+ to the original.
+ 1.3 - Added a new class, CSmtpMimePart, to which the CSmtpAttachment
+ and CSmtpMessageBody classes inherit. This was done for
+ future expansion. CSmtpMimePart has a new ContentId string
+ value for optionally assigning a unique content ID value to
+ body parts and attachments. This was done to support the
+ multipart/related enhancement
+ - Support for multipart/related messages, which can be used
+ for sending html messages with embedded images.
+ - Modifed CSmtpMessage, adding a new MimeType member variable
+ so the user can specify a certain type of MIME format to use
+ when coding the message.
+ - Fixed a bug where multipart/alternative messages with multiple
+ message bodies were not properly processed when attachments
+ were also included in the message.
+ - Some small optimizations during the CSmtpMessage::Parse routine
+
+ 1.2 - Vastly improved the time it takes to break a message,
+ which was dog slow with large attachments. My bad.
+ - Added another overridable, SmtpProgress() which is
+ called during the CSmtp::SendCmd() function when there
+ is a large quantity of data being sent over the wire.
+ Added CMD_BLOCK_SIZE to support the above new feature
+ - Added support for UNICODE
+ - Added the CSmtpAttachment class for better control and
+ expandability for attachments.
+ - Added alternative implementations for CSmtp::SendMessage
+ which make it easier to send simple messages via a single
+ function call.
+ - Added a constructor to CSmtpAddress for assigning default
+ values during initialization.
+ - Added a #pragma comment(lib,"wsock32.lib") to the smtp.h
+ file so existing projects don't have to have their linker
+ options modified.
+
+ 1.1 - Rearranged the headers so they are written out as:
+ From,To,Subject,Date,MimeVersion,
+ followed by all remaining headers
+ - Modified the class to support multipart/alternative with
+ multiple message bodies.
+
+ Note that CSimpleMap does not sort the key values, and CSmtp
+ takes advantage of this by writing the headers out in the reverse
+ order of how they will be parsed before being sent to the SMTP
+ server. If you modify the code to use std::map or any other map
+ class, the headers may be alphabetized by key, which may cause
+ some mail clients to show the headers in the body of the message
+ or cause other undesirable results when viewing the message.
+*/
+//////////////////////////////////////////////////////////////////////
+
+#include "Smtp.h"
+
+//////////////////////////////////////////////////////////////////////
+// Construction/Destruction for CSmtpMessageBody
+//////////////////////////////////////////////////////////////////////
+CSmtpMessageBody::CSmtpMessageBody(LPCTSTR pszBody, LPCTSTR pszEncoding, LPCTSTR pszCharset, EncodingEnum encode)
+{
+
+ // Set the default message encoding method
+ // To transfer html messages, make Encoding = _T("text/html")
+ if (pszEncoding) Encoding = pszEncoding;
+ if (pszCharset) Charset = pszCharset;
+ if (pszBody) Data = pszBody;
+ TransferEncoding = encode;
+}
+
+const CSmtpMessageBody& CSmtpMessageBody::operator=(LPCTSTR pszBody)
+{
+ Data = pszBody;
+ return *this;
+}
+
+const CSmtpMessageBody& CSmtpMessageBody::operator=(const String& strBody)
+{
+ Data = strBody;
+ return *this;
+}
+
+//////////////////////////////////////////////////////////////////////
+// Construction/Destruction for CSmtpAttachment
+//////////////////////////////////////////////////////////////////////
+CSmtpAttachment::CSmtpAttachment(LPCTSTR pszFilename, LPCTSTR pszAltName, BOOL bIsInline, LPCTSTR pszEncoding, LPCTSTR pszCharset, EncodingEnum encode)
+{
+ if (pszFilename) FileName = pszFilename;
+ if (pszAltName) AltName = pszAltName;
+ if (pszEncoding) Encoding = pszEncoding;
+ if (pszCharset) Charset = pszCharset;
+ TransferEncoding = encode;
+ Inline = bIsInline;
+}
+
+const CSmtpAttachment& CSmtpAttachment::operator=(LPCTSTR pszFilename)
+{
+ FileName = pszFilename;
+ return *this;
+}
+
+const CSmtpAttachment& CSmtpAttachment::operator=(const String& strFilename)
+{
+ FileName = strFilename;
+ return *this;
+}
+
+//////////////////////////////////////////////////////////////////////
+// Construction/Destruction for CSmtpAddress
+//////////////////////////////////////////////////////////////////////
+CSmtpAddress::CSmtpAddress(LPCTSTR pszAddress, LPCTSTR pszName)
+{
+ if (pszAddress) Address = pszAddress;
+ if (pszName) Name = pszName;
+}
+
+const CSmtpAddress& CSmtpAddress::operator=(LPCTSTR pszAddress)
+{
+ Address = pszAddress;
+ return *this;
+}
+
+const CSmtpAddress& CSmtpAddress::operator=(const String& strAddress)
+{
+ Address = strAddress;
+ return *this;
+}
+
+//////////////////////////////////////////////////////////////////////
+// Construction/Destruction for CSmtpMessage
+//////////////////////////////////////////////////////////////////////
+CSmtpMessage::CSmtpMessage()
+{
+ TIME_ZONE_INFORMATION tzi;
+ DWORD dwRet;
+ long Offset;
+
+ // Get local time and timezone offset
+ GetLocalTime(&Timestamp);
+ GMTOffset = 0;
+ dwRet = GetTimeZoneInformation(&tzi);
+ Offset = tzi.Bias;
+ if (dwRet == TIME_ZONE_ID_STANDARD) Offset += tzi.StandardBias;
+ if (dwRet == TIME_ZONE_ID_DAYLIGHT) Offset += tzi.DaylightBias;
+ GMTOffset = -((Offset / 60) * 100 + (Offset % 60));
+
+ MimeType = mimeGuess;
+}
+
+// Write all the headers to the e-mail message.
+// This is done just before sending it, when we're sure the user wants it to go out.
+void CSmtpMessage::CommitHeaders()
+{
+ TCHAR szTime[64] = {0};
+ TCHAR szDate[64] = {0};
+ TCHAR szOut[1024] = {0};
+ String strHeader;
+ String strValue;
+ int n;
+
+ // Assign a few standard headers to the message
+ strHeader = _T("X-Priority");
+ strValue = _T("3 (Normal)");
+ // Only add the key if it doesn't exist already in the headers map
+ if (Headers.FindKey(strHeader) == -1) Headers.Add(strHeader,strValue);
+
+ strHeader = _T("X-MSMail-Priority");
+ strValue = _T("Normal");
+ if (Headers.FindKey(strHeader) == -1) Headers.Add(strHeader,strValue);
+
+ strHeader = _T("X-Mailer");
+ strValue = _T("ATL CSmtp Class Mailer by Robert Simpson (robert@blackcastlesoft.com)");
+ if (Headers.FindKey(strHeader) == -1) Headers.Add(strHeader,strValue);
+
+ strHeader = _T("Importance");
+ strValue = _T("Normal");
+ if (Headers.FindKey(strHeader) == -1) Headers.Add(strHeader,strValue);
+
+ // Get the time/date stamp and GMT offset for the Date header.
+ GetDateFormat(MAKELCID(LANG_ENGLISH, SORT_DEFAULT),0,&Timestamp,_T("ddd, d MMM yyyy"),szDate,64);
+ GetTimeFormat(MAKELCID(LANG_ENGLISH, SORT_DEFAULT),0,&Timestamp,_T("H:mm:ss"),szTime,64);
+
+ // Add the date/time stamp to the message headers
+ wsprintf(szOut,_T("%s %s %c%4.4d"),szDate,szTime,(GMTOffset>0)?'+':'-',GMTOffset);
+
+ strHeader = _T("Date");
+ strValue = szOut;
+ Headers.Remove(strHeader);
+ Headers.Add(strHeader,strValue);
+
+ // Write out the subject header
+ strHeader = _T("Subject");
+ strValue = Subject;
+ Headers.Remove(strHeader);
+ Headers.Add(strHeader,strValue);
+
+ // Write out the TO header
+ strValue.erase();
+ strHeader = _T("To");
+ if (Recipient.Name.length())
+ {
+ wsprintf(szOut,_T("\"%s\" "),Recipient.Name.c_str());
+ strValue += szOut;
+ }
+ if (Recipient.Address.length())
+ {
+ wsprintf(szOut,_T("<%s>"),Recipient.Address.c_str());
+ strValue += szOut;
+ }
+ // Write out all the CC'd names
+ for (n = 0;n < CC.GetSize();n++)
+ {
+ if (strValue.length()) strValue += _T(",\r\n\t");
+ if (CC[n].Name.length())
+ {
+ wsprintf(szOut,_T("\"%s\" "),CC[n].Name.c_str());
+ strValue += szOut;
+ }
+ wsprintf(szOut,_T("<%s>"),CC[n].Address.c_str());
+ strValue += szOut;
+ }
+ Headers.Remove(strHeader);
+ Headers.Add(strHeader,strValue);
+
+ // Write out the FROM header
+ strValue.erase();
+ strHeader = _T("From");
+ if (Sender.Name.length())
+ {
+ wsprintf(szOut,_T("\"%s\" "),Sender.Name.c_str());
+ strValue += szOut;
+ }
+ wsprintf(szOut,_T("<%s>"),Sender.Address.c_str());
+ strValue += szOut;
+ Headers.Remove(strHeader);
+ Headers.Add(strHeader,strValue);
+}
+
+// Parse a message into a single string
+void CSmtpMessage::Parse(String& strDest)
+{
+ String strHeader;
+ String strValue;
+ String strTemp;
+ String strBoundary;
+ String strInnerBoundary;
+ TCHAR szOut[1024];
+ int n;
+
+ strDest.erase();
+ // Get a count of the sections to see if this will be a multipart message
+ n = Message.GetSize();
+ n += Attachments.GetSize();
+
+ // Remove this header in case the message is being reused
+ strHeader = _T("Content-Type");
+ Headers.Remove(strHeader);
+
+ // If we have more than one section, then this is a multipart MIME message
+ if (n > 1)
+ {
+ wsprintf(szOut,_T("CSmtpMsgPart123X456_000_%8.8X"),GetTickCount());
+ strBoundary = szOut;
+
+ lstrcpy(szOut,_T("multipart/"));
+
+ if (MimeType == mimeGuess)
+ {
+ if (Attachments.GetSize() == 0) MimeType = mimeAlternative;
+ else MimeType = mimeMixed;
+ }
+ switch(MimeType)
+ {
+ case mimeAlternative:
+ lstrcat(szOut,_T("alternative"));
+ break;
+ case mimeMixed:
+ lstrcat(szOut,_T("mixed"));
+ break;
+ case mimeRelated:
+ lstrcat(szOut,_T("related"));
+ break;
+ }
+ lstrcat(szOut,_T(";\r\n\tboundary=\""));
+ lstrcat(szOut,strBoundary.c_str());
+ lstrcat(szOut,_T("\""));
+
+ strValue = szOut;
+ Headers.Add(strHeader,strValue);
+ }
+
+ strHeader = _T("MIME-Version");
+ strValue = MIME_VERSION;
+ Headers.Remove(strHeader);
+ Headers.Add(strHeader,strValue);
+
+ // Remove any message ID in the header and replace it with this message ID, if it exists
+ strHeader = _T("Message-ID");
+ Headers.Remove(strHeader);
+ if (MessageId.length())
+ {
+ wsprintf(szOut,_T("<%s>"),MessageId.c_str());
+ strValue = szOut;
+ Headers.Add(strHeader,strValue);
+ }
+
+ // Finalize the message headers
+ CommitHeaders();
+
+ // Write out all the message headers -- done backwards on purpose!
+ for (n = Headers.GetSize();n > 0;n--)
+ {
+ wsprintf(szOut,_T("%s: %s\r\n"),Headers.GetKeyAt(n-1).c_str(),Headers.GetValueAt(n-1).c_str());
+ strDest += szOut;
+ }
+ if (strBoundary.length())
+ {
+ wsprintf(szOut,_T("\r\n%s\r\n"),MULTIPART_MESSAGE);
+ strDest += szOut;
+ }
+
+ // If we have attachments and multiple message bodies, create a new multipart section
+ // This is done so we can display our multipart/alternative section separate from the
+ // main multipart/mixed environment, and support both attachments and alternative bodies.
+ if (Attachments.GetSize() && Message.GetSize() > 1 && strBoundary.length())
+ {
+ wsprintf(szOut,_T("CSmtpMsgPart123X456_001_%8.8X"),GetTickCount());
+ strInnerBoundary = szOut;
+
+ wsprintf(szOut,_T("\r\n--%s\r\nContent-Type: multipart/alternative;\r\n\tboundary=\"%s\"\r\n"),strBoundary.c_str(),strInnerBoundary.c_str());
+ strDest += szOut;
+ }
+
+ for (n = 0;n < Message.GetSize();n++)
+ {
+ // If we're multipart, then write the boundary line
+ if (strBoundary.length() || strInnerBoundary.length())
+ {
+ strDest += _T("\r\n--");
+ // If we have an inner boundary, write that one. Otherwise write the outer one
+ if (strInnerBoundary.length()) strDest += strInnerBoundary;
+ else strDest += strBoundary;
+ strDest += _T("\r\n");
+ }
+ strValue.erase();
+ strDest += _T("Content-Type: ");
+ strDest += Message[n].Encoding;
+ // Include the character set if the message is text
+ if (_tcsnicmp(Message[n].Encoding.c_str(),_T("text/"),5) == 0)
+ {
+ wsprintf(szOut,_T(";\r\n\tcharset=\"%s\""),Message[n].Charset.c_str());
+ strDest += szOut;
+ }
+ strDest += _T("\r\n");
+
+ // Encode the message
+ strValue = Message[n].Data;
+ EncodeMessage(Message[n].TransferEncoding,strValue,strTemp);
+
+ // Write out the encoding method used and the encoded message
+ strDest += _T("Content-Transfer-Encoding: ");
+ strDest += strTemp;
+
+ // If the message body part has a content ID, write it out
+ if (Message[n].ContentId.length())
+ {
+ wsprintf(szOut,_T("\r\nContent-ID: <%s>"),Message[n].ContentId.c_str());
+ strDest += szOut;
+ }
+ strDest += _T("\r\n\r\n");
+ strDest += strValue;
+ }
+
+ // If we have multiple message bodies, write out the trailing inner end sequence
+ if (strInnerBoundary.length())
+ {
+ wsprintf(szOut,_T("\r\n--%s--\r\n"),strInnerBoundary.c_str());
+ strDest += szOut;
+ }
+
+ // Process any attachments
+ for (n = 0;n < Attachments.GetSize();n++)
+ {
+ DWORD dwBytes = 0;
+ CRegKey cKey;
+ TCHAR szFilename[MAX_PATH] = {0};
+
+ // Get the filename of the attachment
+ strValue = Attachments[n].FileName;
+
+ // Open the file
+ lstrcpy(szFilename,strValue.c_str());
+ HANDLE hFile = CreateFile(szFilename,GENERIC_READ,0,NULL,OPEN_EXISTING,0,NULL);
+ if (hFile != INVALID_HANDLE_VALUE)
+ {
+ // Get the size of the file, allocate the memory and read the contents.
+ DWORD dwSize = GetFileSize(hFile,NULL);
+ LPBYTE pData = (LPBYTE)malloc(dwSize + 1);
+ ZeroMemory(pData,dwSize+1);
+
+ if (ReadFile(hFile,pData,dwSize,&dwBytes,NULL))
+ {
+ // Write out our boundary marker
+ if (strBoundary.length())
+ {
+ wsprintf(szOut,_T("\r\n--%s\r\n"),strBoundary.c_str());
+ strDest += szOut;
+ }
+
+ // If no alternate name is supplied, strip the path to get the base filename
+ LPTSTR pszFile;
+ if (!Attachments[n].AltName.length())
+ {
+ // Strip the path from the filename
+ pszFile = _tcsrchr(szFilename,'\\');
+ if (!pszFile) pszFile = szFilename;
+ else pszFile ++;
+ }
+ else pszFile = (LPTSTR)Attachments[n].AltName.c_str();
+
+ // Set the content type for the attachment.
+ TCHAR szType[MAX_PATH] = {0};
+ lstrcpy(szType,_T("application/octet-stream"));
+
+ // Check the registry for a content type that overrides the above default
+ LPTSTR pszExt = _tcschr(pszFile,'.');
+ if (pszExt)
+ {
+ if (!cKey.Open(HKEY_CLASSES_ROOT,pszExt,KEY_READ))
+ {
+ DWORD dwSize = MAX_PATH;
+ cKey.QueryValue(_T("Content Type"), NULL, szType, &dwSize);
+ cKey.Close();
+ }
+ }
+
+ // If the attachment has a specific encoding method, use it instead
+ if (Attachments[n].Encoding.length())
+ lstrcpy(szType,Attachments[n].Encoding.c_str());
+
+ // Write out the content type and attachment types to the message
+ wsprintf(szOut,_T("Content-Type: %s"),szType);
+ strDest += szOut;
+ // If the content type is text, write the charset
+ if (_tcsnicmp(szType,_T("text/"),5) == 0)
+ {
+ wsprintf(szOut,_T(";\r\n\tcharset=\"%s\""),Attachments[n].Charset.c_str());
+ strDest += szOut;
+ }
+ wsprintf(szOut,_T(";\r\n\tname=\"%s\"\r\n"),pszFile);
+ strDest += szOut;
+
+ // Encode the attachment
+ EncodeMessage(Attachments[n].TransferEncoding,strValue,strTemp,pData,dwSize);
+
+ // Write out the transfer encoding method
+ wsprintf(szOut,_T("Content-Transfer-Encoding: %s\r\n"),strTemp.c_str());
+ strDest += szOut;
+
+ // Write out the attachment's disposition
+ strDest += _T("Content-Disposition: ");
+
+ if (Attachments[n].Inline) strDest += _T("inline");
+ else strDest += _T("attachment");
+
+ strDest += _T(";\r\n\tfilename=\"");
+ strDest += pszFile;
+
+ // If the attachment has a content ID, write it out
+ if (Attachments[n].ContentId.length())
+ {
+ wsprintf(szOut,_T("\r\nContent-ID: <%s>"),Attachments[n].ContentId.c_str());
+ strDest += szOut;
+ }
+ strDest += _T("\r\n\r\n");
+
+ // Write out the encoded attachment
+ strDest += strValue;
+ strTemp.erase();
+ strValue.erase();
+ }
+ // Close the file and clear the temp buffer
+ CloseHandle(hFile);
+ free(pData);
+ }
+ }
+
+ // If we are multipart, write out the trailing end sequence
+ if (strBoundary.length())
+ {
+ wsprintf(szOut,_T("\r\n--%s--\r\n"),strBoundary.c_str());
+ strDest += szOut;
+ }
+}
+
+// Parses text into quoted-printable lines.
+// See RFC 1521 for full details on how this works.
+void CSmtpMessage::EncodeQuotedPrintable(String& strDest, String& strSrc)
+{
+ String strTemp;
+ String strTemp2;
+ LPTSTR pszTok1;
+ LPTSTR pszTok2;
+ TCHAR szSub[16];
+ TCHAR ch;
+ int n;
+
+ strDest.erase();
+ if (!strSrc.length()) return;
+
+ // Change = signs and non-printable characters to =XX
+ pszTok1 = (LPTSTR)strSrc.c_str();
+ pszTok2 = pszTok1;
+ do
+ {
+ if (*pszTok2 == '=' || *pszTok2 > 126 ||
+ (*pszTok2 < 32 && (*pszTok2 != '\r' && *pszTok2 != '\n' && *pszTok2 != '\t')))
+ {
+ ch = *pszTok2;
+ *pszTok2 = 0;
+ strTemp += pszTok1;
+ *pszTok2 = ch;
+ wsprintf(szSub,_T("=%2.2X"),(BYTE)*pszTok2);
+ strTemp += szSub;
+ pszTok1 = pszTok2 + 1;
+ }
+ pszTok2 ++;
+ } while (pszTok2 && *pszTok2);
+
+ // Append anything left after the search
+ if (_tcslen(pszTok1)) strTemp += pszTok1;
+
+ pszTok1 = (LPTSTR)strTemp.c_str();
+ while (pszTok1)
+ {
+ pszTok2 = _tcschr(pszTok1,'\r');
+ if (pszTok2) *pszTok2 = 0;
+ while (1)
+ {
+ if (_tcslen(pszTok1) > 76)
+ {
+ n = 75; // Breaking at the 75th character
+ if (pszTok1[n-1] == '=') n -= 1; // If the last character is an =, don't break the line there
+ else if (pszTok1[n-2] == '=') n -= 2; // If we're breaking in the middle of a = sequence, back up!
+
+ // Append the first section of the line to the total string
+ ch = pszTok1[n];
+ pszTok1[n] = 0;
+ strDest += pszTok1;
+ pszTok1[n] = ch;
+ strDest += _T("=\r\n");
+ pszTok1 += n;
+ }
+ else // Line is less than or equal to 76 characters
+ {
+ n = (int)_tcslen(pszTok1); // If we have some trailing data, process it.
+ if (n)
+ {
+ if (pszTok1[n-1] == ' ' || pszTok1[n-1] == '\t') // Last character is a space or tab
+ {
+ wsprintf(szSub,_T("=%2.2X"),(BYTE)pszTok1[n-1]);
+ // Replace the last character with an =XX sequence
+ pszTok1[n-1] = 0;
+ strTemp2 = pszTok1;
+ strTemp2 += szSub;
+ // Since the string may now be larger than 76 characters, we have to reprocess the line
+ pszTok1 = (LPTSTR)strTemp2.c_str();
+ }
+ else // Last character is not a space or tab
+ {
+ strDest += pszTok1;
+ if (pszTok2) strDest += _T("\r\n");
+ break; // Exit the loop which processes this line, and move to the next line
+ }
+ }
+ else
+ {
+ if (pszTok2) strDest += _T("\r\n");
+ break; // Move to the next line
+ }
+ }
+ }
+ if (pszTok2)
+ {
+ *pszTok2 = '\r';
+ pszTok2 ++;
+ if (*pszTok2 == '\n') pszTok2 ++;
+ }
+ pszTok1 = pszTok2;
+ }
+}
+
+// Breaks a message's lines into a maximum of 76 characters
+// Does some semi-intelligent wordwrapping to ensure the text is broken properly.
+// If a line contains no break characters, it is forcibly truncated at the 76th char
+void CSmtpMessage::BreakMessage(String& strDest, String& strSrc, int nLength)
+{
+ String strTemp = strSrc;
+ LPTSTR pszTok1;
+ LPTSTR pszTok2;
+ LPTSTR pszBreak;
+ LPTSTR pszBreaks = _T(" -;.,?!");
+ TCHAR ch;
+ int nLen;
+
+ strDest.erase();
+ if (!strSrc.length()) return;
+
+ nLen = (int)strTemp.length();
+ nLen += (nLen / 60) * 2;
+
+ strDest.reserve(nLen);
+
+ // Process each line one at a time
+ pszTok1 = (LPTSTR)strTemp.c_str();
+ while (pszTok1)
+ {
+ pszTok2 = _tcschr(pszTok1,'\r');
+ if (pszTok2) *pszTok2 = 0;
+
+ BOOL bNoBreaks = (!_tcspbrk(pszTok1,pszBreaks));
+ nLen = (int)_tcslen(pszTok1);
+ while (nLen > nLength)
+ {
+ // Start at the 76th character, and move backwards until we hit a break character
+ pszBreak = &pszTok1[nLength - 1];
+
+ // If there are no break characters in the string, skip the backward search for them!
+ if (!bNoBreaks)
+ {
+ while (!_tcschr(pszBreaks,*pszBreak) && pszBreak > pszTok1)
+ pszBreak--;
+ }
+ pszBreak ++;
+ ch = *pszBreak;
+ *pszBreak = 0;
+ strDest += pszTok1;
+
+ strDest += _T("\r\n");
+ *pszBreak = ch;
+
+ nLen -= (int)(pszBreak - pszTok1);
+ // Advance the search to the next segment of text after the break
+ pszTok1 = pszBreak;
+ }
+ strDest += pszTok1;
+ if (pszTok2)
+ {
+ strDest += _T("\r\n");
+ *pszTok2 = '\r';
+ pszTok2 ++;
+ if (*pszTok2 == '\n') pszTok2 ++;
+ }
+ pszTok1 = pszTok2;
+ }
+}
+
+// Makes the message into a 7bit stream
+void CSmtpMessage::Make7Bit(String& strDest, String& strSrc)
+{
+ LPTSTR pszTok;
+
+ strDest = strSrc;
+
+ pszTok = (LPTSTR)strDest.c_str();
+ do
+ {
+ // Replace any characters above 126 with a ? character
+ if (*pszTok > 126 || *pszTok < 0)
+ *pszTok = '?';
+ pszTok ++;
+ } while (pszTok && *pszTok);
+}
+
+// Encodes a message or binary stream into a properly-formatted message
+// Takes care of breaking the message into 76-byte lines of text, encoding to
+// Base64, quoted-printable and etc.
+void CSmtpMessage::EncodeMessage(EncodingEnum code, String& strMsg, String& strMethod, LPBYTE pByte, DWORD dwSize)
+{
+ String strTemp;
+ LPTSTR pszTok1;
+ LPTSTR pszTok2;
+ LPSTR pszBuffer = NULL;
+ DWORD dwStart = GetTickCount();
+
+ if (!pByte)
+ {
+ pszBuffer = (LPSTR)malloc(strMsg.length() + 1);
+ _T2A(pszBuffer,strMsg.c_str());
+ pByte = (LPBYTE)pszBuffer;
+ dwSize = (DWORD)strMsg.length();
+ }
+
+ // Guess the encoding scheme if we have to
+ if (code == encodeGuess) code = GuessEncoding(pByte, dwSize);
+
+ switch(code)
+ {
+ case encodeQuotedPrintable:
+ strMethod = _T("quoted-printable");
+
+ pszTok1 = (LPTSTR)malloc((dwSize+1) * sizeof(TCHAR));
+ _A2T(pszTok1,(LPSTR)pByte);
+ strMsg = pszTok1;
+ free(pszTok1);
+
+ EncodeQuotedPrintable(strTemp, strMsg);
+ break;
+ case encodeBase64:
+ strMethod = _T("base64");
+ {
+ CBase64 cvt;
+ cvt.Encode(pByte, dwSize);
+ LPSTR pszTemp = (LPSTR)cvt.EncodedMessage();
+ pszTok1 = (LPTSTR)malloc((lstrlenA(pszTemp)+1) * sizeof(TCHAR));
+ _A2T(pszTok1,pszTemp);
+ }
+ strMsg = pszTok1;
+ free(pszTok1);
+
+ BreakMessage(strTemp, strMsg);
+ break;
+ case encode7Bit:
+ strMethod = _T("7bit");
+
+ pszTok1 = (LPTSTR)malloc((dwSize+1) * sizeof(TCHAR));
+ _A2T(pszTok1,(LPSTR)pByte);
+ strMsg = pszTok1;
+ free(pszTok1);
+
+ Make7Bit(strTemp, strMsg);
+ strMsg = strTemp;
+ BreakMessage(strTemp, strMsg);
+ break;
+ case encode8Bit:
+ strMethod = _T("8bit");
+
+ pszTok1 = (LPTSTR)malloc((dwSize+1) * sizeof(TCHAR));
+ _A2T(pszTok1,(LPSTR)pByte);
+ strMsg = pszTok1;
+ free(pszTok1);
+
+ BreakMessage(strTemp, strMsg);
+ break;
+ }
+
+ if (pszBuffer) free(pszBuffer);
+
+ strMsg.erase();
+
+ // Parse the message text, replacing CRLF. sequences with CRLF.. sequences
+ pszTok1 = (LPTSTR)strTemp.c_str();
+ do
+ {
+ pszTok2 = _tcsstr(pszTok1,_T("\r\n."));
+ if (pszTok2)
+ {
+ *pszTok2 = 0;
+ strMsg += pszTok1;
+ *pszTok2 = '\r';
+ strMsg += _T("\r\n..");
+ pszTok1 = pszTok2 + 3;
+ }
+ } while (pszTok2);
+ strMsg += pszTok1;
+
+ TCHAR szOut[MAX_PATH] = {0};
+ wsprintf(szOut,_T("Encoding took %dms\n"),GetTickCount() - dwStart);
+ OutputDebugString(szOut);
+}
+
+// Makes a best-guess of the proper encoding to use for this stream of bytes
+// It does this by counting the # of lines, the # of 8bit bytes and the number
+// of 7bit bytes. It also records the line and the count of lines over
+// 76 characters.
+// If the stream is 90% or higher 7bit, it uses a text encoding method. If the stream
+// is all at or under 76 characters, it uses 7bit or 8bit, depending on the content.
+// If the lines are longer than 76 characters, use quoted printable.
+// If the stream is under 90% 7bit characters, use base64 encoding.
+EncodingEnum CSmtpMessage::GuessEncoding(LPBYTE pByte, DWORD dwLen)
+{
+ int n7Bit = 0;
+ int n8Bit = 0;
+ int nLineStart = 0;
+ int nLinesOver76 = 0;
+ int nLines = 0;
+ DWORD n;
+
+ // Count the content type, byte by byte
+ for (n = 0;n < dwLen; n++)
+ {
+ if (pByte[n] > 126 || (pByte[n] < 32 && pByte[n] != '\t' && pByte[n] != '\r' && pByte[n] != '\n'))
+ n8Bit ++;
+ else n7Bit ++;
+
+ // New line? If so, record the line size
+ if (pByte[n] == '\r')
+ {
+ nLines ++;
+ nLineStart = (n - nLineStart) - 1;
+ if (nLineStart > 76) nLinesOver76 ++;
+ nLineStart = n + 1;
+ }
+ }
+ // Determine if it is mostly 7bit data
+ if ((n7Bit * 100) / dwLen > 89)
+ {
+ // At least 90% text, so use a text-base encoding scheme
+ if (!nLinesOver76)
+ {
+ if (!n8Bit) return encode7Bit;
+ else return encode8Bit;
+ }
+ else return encodeQuotedPrintable;
+ }
+ return encodeBase64;
+}
+
+//////////////////////////////////////////////////////////////////////
+// Construction/Destruction for CSmtp
+//////////////////////////////////////////////////////////////////////
+CSmtp::CSmtp()
+{
+ LPSERVENT pEnt;
+
+ m_bExtensions = TRUE; // Use ESMTP if possible
+ m_dwCmdTimeout = 30; // Default to 30 second timeout
+ m_hSocket = INVALID_SOCKET;
+ m_bConnected = m_bUsingExtensions = FALSE;
+
+ // Try and get the SMTP service entry by name
+ pEnt = getservbyname("SMTP","tcp");
+ if (pEnt) m_wSmtpPort = pEnt->s_port;
+ else m_wSmtpPort = htons(25);
+
+}
+
+CSmtp::~CSmtp()
+{
+ // Make sure any open connections are shut down
+ Close();
+}
+
+// Connects to a SMTP server. Returns TRUE if successfully connected, or FALSE otherwise.
+BOOL CSmtp::Connect(LPTSTR pszServer)
+{
+ SOCKADDR_IN addr;
+ int nRet;
+ CHAR szHost[MAX_PATH] = {0};
+
+ _T2A(szHost,pszServer);
+ // Shut down any active connection
+ Close();
+// test
+ WORD wVersionRequested;
+ WSADATA wsaData;
+ wVersionRequested = MAKEWORD( 2, 1 );
+ WSAStartup( wVersionRequested, &wsaData );
+// end test
+ // Resolve the hostname
+ addr.sin_family = AF_INET;
+ addr.sin_port = m_wSmtpPort;
+ addr.sin_addr.s_addr = inet_addr(szHost);
+
+ if (addr.sin_addr.s_addr == INADDR_NONE)
+ {
+
+ LPHOSTENT pHost = gethostbyname(szHost);
+ if (!pHost)
+ {
+ return FALSE;
+ }
+
+ addr.sin_addr.s_addr = *(LPDWORD)pHost->h_addr;
+ }
+
+
+
+ // Create a socket
+ m_hSocket = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
+ if (m_hSocket == INVALID_SOCKET) return FALSE;
+
+ // Connect to the host
+ if (connect(m_hSocket,(LPSOCKADDR)&addr,sizeof(addr)) == SOCKET_ERROR)
+ {
+ Close();
+ return FALSE;
+ }
+
+ // Get the initial response string
+ nRet = SendCmd(NULL);
+ if (nRet != 220)
+ {
+ RaiseError(nRet);
+ Close();
+ return FALSE;
+ }
+
+ // Send a HELLO message to the SMTP server
+ if (SendHello())
+ {
+ Close();
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+// Closes any active SMTP sessions and shuts down the socket.
+void CSmtp::Close()
+{
+ if (m_hSocket != INVALID_SOCKET)
+ {
+ // If we're connected to a server, tell them we're quitting
+ if (m_bConnected) SendQuitCmd();
+ // Shutdown and close the socket
+ shutdown(m_hSocket,2);
+ closesocket(m_hSocket);
+ }
+ m_hSocket = INVALID_SOCKET;
+}
+
+// Send a command to the SMTP server and wait for a response
+int CSmtp::SendCmd(LPTSTR pszCmd)
+{
+ USES_CONVERSION;
+ FD_SET set;
+ TIMEVAL tv;
+ int nRet = 0;
+ DWORD dwTick;
+ CHAR szResult[CMD_RESPONSE_SIZE] = {0};
+ LPSTR pszPos;
+ LPSTR pszTok;
+ BOOL bReportProgress = FALSE;
+ LPSTR pszBuff;
+
+ ZeroMemory(szResult,CMD_RESPONSE_SIZE);
+ FD_ZERO(&set);
+
+ // If we have a command to send, then send it.
+ if (pszCmd)
+ {
+ pszBuff = (LPSTR)malloc(lstrlen(pszCmd)+1);
+ _T2A(pszBuff,pszCmd);
+
+ // Make sure the input buffer is clear before sending
+ nRet = 1;
+ while (nRet > 0)
+ {
+ FD_SET(m_hSocket,&set);
+ tv.tv_sec = 0;
+ tv.tv_usec = 0;
+ nRet = select(1,&set,NULL,NULL,&tv);
+ if (nRet == 1) nRet = recv(m_hSocket,szResult,CMD_RESPONSE_SIZE,0);
+ }
+ DWORD dwPosition = 0;
+ DWORD dwLen = lstrlen(pszCmd);
+ if (dwLen > CMD_BLOCK_SIZE) bReportProgress = TRUE;
+
+ while (dwLen != dwPosition)
+ {
+ DWORD dwMax = min(CMD_BLOCK_SIZE,dwLen - dwPosition);
+ nRet = send(m_hSocket,&pszBuff[dwPosition],dwMax,0);
+ if (nRet == SOCKET_ERROR)
+ {
+ free(pszBuff);
+ return nRet;
+ }
+ dwPosition += dwMax;
+ if (bReportProgress)
+ {
+ if (!SmtpProgress(pszBuff,dwPosition,dwLen))
+ {
+ free(pszBuff);
+ return -1;
+ }
+ }
+ }
+ // Wait for the CMD to finish being sent
+ FD_ZERO(&set);
+ FD_SET(m_hSocket,&set);
+ nRet = select(1,NULL,&set,NULL,NULL);
+ free(pszBuff);
+ }
+
+ // Prepare to receive a response
+ ZeroMemory(szResult,CMD_RESPONSE_SIZE);
+ pszPos = szResult;
+ // Wait for the specified timeout for a full response string
+ dwTick = GetTickCount();
+ while (GetTickCount() - dwTick < (m_dwCmdTimeout * 1000))
+ {
+ FD_SET(m_hSocket,&set);
+
+ tv.tv_sec = m_dwCmdTimeout - ((GetTickCount() - dwTick) / 1000);
+ tv.tv_usec = 0;
+
+ // Check the socket for readability
+ nRet = select(1,&set,NULL,NULL,&tv);
+ if (nRet == SOCKET_ERROR) break;
+
+ // If the socket has data, read it.
+ if (nRet == 1)
+ {
+ nRet = recv(m_hSocket,pszPos,CMD_RESPONSE_SIZE - (int)(pszPos - szResult),0);
+ // Treats a graceful shutdown as an error
+ if (nRet == 0) nRet = SOCKET_ERROR;
+ if (nRet == SOCKET_ERROR) break;
+
+ // Add the data to the total response string & check for a LF
+ pszPos += nRet;
+ pszTok = strrchr(szResult,'\n');
+ if (pszTok)
+ {
+ // Truncate CRLF combination and exit our wait loop
+ pszTok --;
+ pszTok[0] = 0;
+ break;
+ }
+ }
+ }
+ // Assign the response string
+ m_strResult = A2CT(szResult);
+
+ // Evaluate the numeric response code
+ if (nRet && nRet != SOCKET_ERROR)
+ {
+ szResult[3] = 0;
+ nRet = atoi(szResult);
+ SmtpCommandResponse(pszCmd, nRet, (LPTSTR)m_strResult.c_str());
+ }
+ else nRet = -1;
+
+ return nRet;
+}
+
+// Placeholder function -- overridable
+// This function is called when the SMTP server gives us an unexpected error
+// The <nError> value is the SMTP server's numeric error response, and the <pszErr>
+// is the descriptive error text
+//
+// <pszErr> may be NULL if the server failed to respond before the timeout!
+// <nError> will be -1 if a catastrophic failure occurred.
+//
+// Return 0, or nError. The return value is currently ignored.
+int CSmtp::SmtpError(int /*nError*/, LPTSTR pszErr)
+{
+#ifdef _DEBUG
+ if (pszErr)
+ {
+ OutputDebugString(_T("SmtpError: "));
+ OutputDebugString(pszErr);
+ OutputDebugString(_T("\n"));
+ }
+#endif
+ return 0;
+}
+
+// Placeholder function -- overridable
+// Currently the only warning condition that this class is designed for is
+// an authentication failure. In that case, <nWarning> will be 535,
+// which is the RFC error for authentication failure. If authentication
+// fails, you can override this function to prompt the user for a new
+// username and password. Change the <m_strUser> and <m_strPass> member
+// variables and return TRUE to retry authentication.
+//
+// <pszWarning> may be NULL if the server did not respond in time!
+//
+// Return FALSE to abort authentication, or TRUE to retry.
+int CSmtp::SmtpWarning(int /*nWarning*/, LPTSTR pszWarning)
+{
+#ifdef _DEBUG
+ if (pszWarning)
+ {
+ OutputDebugString(_T("SmtpWarning: "));
+ OutputDebugString(pszWarning);
+ OutputDebugString(_T("\n"));
+ }
+#endif
+ return 0;
+}
+
+// Placeholder function -- overridable
+// This is an informational callback only, and provides a means to inform
+// the caller as the SMTP session progresses.
+// ALWAYS check for NULL values on <pszCmd> and <pszResponse> before performing
+// any actions!
+// <nResponse> will be -1 if a catastrophic failure occurred, but that will
+// be raised in the SmtpError() event later on during processing.
+void CSmtp::SmtpCommandResponse(LPTSTR pszCmd, int /*nResponse*/, LPTSTR pszResponse)
+{
+#ifdef _DEBUG
+ if (pszCmd)
+ {
+ TCHAR szOut[MAX_PATH+1] = {0};
+ OutputDebugString(_T("SmtpCommand : "));
+ while (lstrlen(pszCmd) > MAX_PATH)
+ {
+ lstrcpyn(szOut,pszCmd,MAX_PATH+1);
+ OutputDebugString(szOut);
+ Sleep(100);
+ pszCmd += MAX_PATH;
+ }
+ OutputDebugString(pszCmd);
+ }
+ OutputDebugString(_T("SmtpResponse: "));
+ OutputDebugString(pszResponse);
+ OutputDebugString(_T("\n"));
+#endif
+}
+
+// Placeholder function -- overridable
+// This is a progress callback to indicate that data is being sent over the wire
+// and that the operation may take some time.
+// Return TRUE to continue sending, or FALSE to abort the transfer
+BOOL CSmtp::SmtpProgress(LPSTR /*pszBuffer*/, DWORD /*dwBytesSent*/, DWORD /*dwBytesTotal*/)
+{
+ return TRUE; // Continue sending the data
+}
+
+// Raises a SmtpError() condition
+int CSmtp::RaiseError(int nError)
+{
+ // If the error code is -1, something catastrophic happened
+ // so we're effectively not connected to any SMTP server.
+ if (nError == -1) m_bConnected = FALSE;
+ return SmtpError(nError, (LPTSTR)m_strResult.c_str());
+}
+
+// Warnings are recoverable errors that we may be able to continue working with
+int CSmtp::RaiseWarning(int nWarning)
+{
+ return SmtpWarning(nWarning, (LPTSTR)m_strResult.c_str());
+}
+
+// E-Mail's a message
+// Returns 0 if successful, -1 if an internal error occurred, or a positive
+// error value if the SMTP server gave an error or failure response.
+int CSmtp::SendMessage(CSmtpMessage &msg)
+{
+ int nRet;
+ int n;
+// int nRecipients = 0;
+ int nRecipientCount = 0;
+
+ // Check if we have a sender
+ if (!msg.Sender.Address.length()) return -1;
+
+ // Check if we have recipients
+ if (!msg.Recipient.Address.length() && !msg.CC.GetSize()) return -1;
+
+ // Check if we have a message body or attachments
+ // *** Commented out to remove the requirement that a message have a body or attachments
+ // if (!msg.Message.GetSize() && !msg.Attachments.GetSize()) return -1;
+
+ // Send the sender's address
+ nRet = SendFrom((LPTSTR)msg.Sender.Address.c_str());
+ if (nRet) return nRet;
+ // If we have a recipient, send it
+ nRecipientCount = 0; // Count of recipients
+ if (msg.Recipient.Address.length())
+ {
+ nRet = SendTo((LPTSTR)msg.Recipient.Address.c_str());
+ if (!nRet) nRecipientCount ++;
+ }
+
+ // If we have any CC's, send those.
+ for (n = 0;n < msg.CC.GetSize();n++)
+ {
+ nRet = SendTo((LPTSTR)msg.CC[n].Address.c_str());
+ if (!nRet) nRecipientCount ++;
+ }
+
+ // If we have any bcc's, send those.
+ for (n = 0;n < msg.BCC.GetSize();n++)
+ {
+ nRet = SendTo((LPTSTR)msg.BCC[n].Address.c_str());
+ if (!nRet) nRecipientCount ++;
+ }
+ // If we failed on all recipients, we must abort.
+ if (!nRecipientCount)
+ RaiseError(nRet);
+ else
+ nRet = SendData(msg);
+
+ return nRet;
+}
+
+// Simplified way to send a message.
+// <pvAttachments> can be either an LPTSTR containing NULL terminated strings, in which
+// case <dwAttachmentCount> should be zero, or <pvAttachments> can be an LPTSTR *
+// containing an array of LPTSTR's, in which case <dwAttachmentCount> should equal the
+// number of strings in the array.
+int CSmtp::SendMessage(CSmtpAddress &addrFrom, CSmtpAddress &addrTo, LPCTSTR pszSubject, LPTSTR pszMessage, LPVOID pvAttachments, DWORD dwAttachmentCount)
+{
+ CSmtpMessage message;
+ CSmtpMessageBody body;
+ CSmtpAttachment attach;
+
+ body = pszMessage;
+
+ message.Sender = addrFrom;
+ message.Recipient = addrTo;
+ message.Message.Add(body);
+ message.Subject = pszSubject;
+
+ // If the attachment count is zero, but the pvAttachments variable is not NULL,
+ // assume that the ppvAttachments variable is a string value containing NULL terminated
+ // strings. A double NULL ends the list.
+ // Example: LPTSTR pszAttachments = "foo.exe\0bar.zip\0autoexec.bat\0\0";
+ if (!dwAttachmentCount && pvAttachments)
+ {
+ LPTSTR pszAttachments = (LPTSTR)pvAttachments;
+ while (lstrlen(pszAttachments))
+ {
+ attach.FileName = pszAttachments;
+ message.Attachments.Add(attach);
+ pszAttachments = &pszAttachments[lstrlen(pszAttachments)];
+ }
+ }
+
+ // dwAttachmentCount is not zero, so assume pvAttachments is an array of LPTSTR's
+ // Example: LPTSTR *ppszAttachments = {"foo.exe","bar.exe","autoexec.bat"};
+ if (pvAttachments && dwAttachmentCount)
+ {
+ LPTSTR *ppszAttachments = (LPTSTR *)pvAttachments;
+ while (dwAttachmentCount-- && ppszAttachments)
+ {
+ attach.FileName = ppszAttachments[dwAttachmentCount];
+ message.Attachments.Add(attach);
+ }
+ }
+ return SendMessage(message);
+}
+
+// Yet an even simpler method for sending a message
+// <pszAddrFrom> and <pszAddrTo> should be e-mail addresses with no decorations
+// Example: "foo@bar.com"
+// <pvAttachments> and <dwAttachmentCount> are described above in the alternative
+// version of this function
+int CSmtp::SendMessage(LPTSTR pszAddrFrom, LPTSTR pszAddrTo, LPTSTR pszSubject, LPTSTR pszMessage, LPVOID pvAttachments, DWORD dwAttachmentCount)
+{
+ CSmtpAddress addrFrom(pszAddrFrom);
+ CSmtpAddress addrTo(pszAddrTo);
+
+ return SendMessage(addrFrom,addrTo,pszSubject,pszMessage,pvAttachments,dwAttachmentCount);
+}
+
+// Tell the SMTP server we're quitting
+// Returns 0 if successful, or a positive
+// error value if the SMTP server gave an error or failure response.
+int CSmtp::SendQuitCmd()
+{
+ int nRet;
+
+ if (!m_bConnected) return 0;
+
+ nRet = SendCmd(_T("QUIT\r\n"));
+ if (nRet != 221) RaiseError(nRet);
+
+ m_bConnected = FALSE;
+
+ return (nRet == 221) ? 0:nRet;
+}
+
+// Initiate a conversation with the SMTP server
+// Returns 0 if successful, or a positive
+// error value if the SMTP server gave an error or failure response.
+int CSmtp::SendHello()
+{
+ int nRet = 0;
+ TCHAR szName[64] = {0};
+ TCHAR szMsg[MAX_PATH] = {0};
+ DWORD dwSize = 64;
+
+ GetComputerName(szName,&dwSize);
+
+ // First try a EHLO if we're using ESMTP
+ wsprintf(szMsg,_T("EHLO %s\r\n"),szName);
+ if (m_bExtensions) nRet = SendCmd(szMsg);
+
+ // If we got a 250 response, we're using ESMTP, otherwise revert to regular SMTP
+ if (nRet != 250)
+ {
+ m_bUsingExtensions = FALSE;
+ szMsg[0] = 'H';
+ szMsg[1] = 'E';
+ nRet = SendCmd(szMsg);
+ }
+ else m_bUsingExtensions = TRUE;
+
+ // Raise any unexpected responses
+ if (nRet != 250)
+ {
+ RaiseError(nRet);
+ return nRet;
+ }
+
+ // We're connected!
+ m_bConnected = TRUE;
+
+ // Send authentication if we have any.
+ // We don't fail just because authentication failed, however.
+ if (m_bUsingExtensions) SendAuthentication();
+
+ return 0;
+}
+
+// Requests authentication for the session if the server supports it,
+// and attempts to submit the user's credentials.
+// Returns 0 if successful, or a positive
+// error value if the SMTP server gave an error or failure response.
+int CSmtp::SendAuthentication()
+{
+ USES_CONVERSION;
+ int nRet = 0;
+ CBase64 cvt;
+ LPCSTR pszTemp;
+ TCHAR szMsg[MAX_PATH] = {0};
+ CHAR szAuthType[MAX_PATH] = {0};
+
+ // This is an authentication loop, we can authenticate multiple times in case of failure.
+ while(1)
+ {
+ // If we don't have a username, skip authentication
+ if (!m_strUser.length()) return 0;
+
+ // Make the authentication request
+ nRet = SendCmd(_T("AUTH LOGIN\r\n"));
+ // If it was rejected, we have to abort.
+ if (nRet != 334)
+ {
+ RaiseWarning(nRet);
+ return nRet;
+ }
+
+ // Authentication has 2 stages for username and password.
+ // It is possible if the authentication fails here that we can
+ // resubmit proper credentials.
+ while (1)
+ {
+ // Decode the authentication string being requested
+ _T2A(szAuthType,&(m_strResult.c_str())[4]);
+
+ cvt.Decode(szAuthType);
+ pszTemp = cvt.DecodedMessage();
+
+ if (!lstrcmpiA(pszTemp,"Username:"))
+ cvt.Encode(T2CA(m_strUser.c_str()));
+ else if (!lstrcmpiA(pszTemp,"Password:"))
+ cvt.Encode(T2CA(m_strPass.c_str()));
+ else break;
+
+ wsprintf(szMsg,_T("%s\r\n"),A2CT(cvt.EncodedMessage()));
+ nRet = SendCmd(szMsg);
+
+ // If we got a failed authentication request, raise a warning.
+ // this gives the owner a chance to change the username and password.
+ if (nRet == 535)
+ {
+ // Return FALSE to fail, or TRUE to retry
+ nRet = RaiseWarning(nRet);
+ if (!nRet)
+ {
+ // Reset the error back to 535. It's now an error rather than a warning
+ nRet = 535;
+ break;
+ }
+ }
+ // Break on any response other than 334, which indicates a request for more information
+ if (nRet != 334) break;
+ }
+ // Break if we're not retrying a failed authentication
+ if (nRet != TRUE) break;
+ }
+ // Raise an error if we failed to authenticate
+ if (nRet != 235) RaiseError(nRet);
+
+ return (nRet == 235) ? 0:nRet;
+}
+
+// Send a MAIL FROM command to the server
+// Returns 0 if successful, or a positive
+// error value if the SMTP server gave an error or failure response.
+int CSmtp::SendFrom(LPTSTR pszFrom)
+{
+ int nRet = 0;
+ TCHAR szMsg[MAX_PATH] = {0};
+
+ wsprintf(szMsg,_T("MAIL FROM: <%s>\r\n"),pszFrom);
+
+ while (1)
+ {
+ nRet = SendCmd(szMsg);
+ // Send authentication if required, and retry the command
+ if (nRet == 530) nRet = SendAuthentication();
+ else break;
+ }
+ // Raise an error if we failed
+ if (nRet != 250) RaiseError(nRet);
+ return (nRet == 250) ? 0:nRet;
+}
+
+// Send a RCPT TO command to the server
+// Returns 0 if successful, or a positive
+// error value if the SMTP server gave an error or failure response.
+int CSmtp::SendTo(LPTSTR pszTo)
+{
+ int nRet;
+ TCHAR szMsg[MAX_PATH] = {0};
+
+ wsprintf(szMsg,_T("RCPT TO: <%s>\r\n"),pszTo);
+ nRet = SendCmd(szMsg);
+ if (nRet != 250 && nRet != 251) RaiseWarning(nRet);
+ return (nRet == 250 || nRet == 251) ? 0:nRet;
+}
+
+// Send the body of an e-mail message to the server
+// Returns 0 if successful, or a positive
+// error value if the SMTP server gave an error or failure response.
+int CSmtp::SendData(CSmtpMessage &msg)
+{
+ int nRet;
+ String strMsg;
+
+ // Send the DATA command. We need a 354 to proceed
+ nRet = SendCmd(_T("DATA\r\n"));
+ if (nRet != 354)
+ {
+ RaiseError(nRet);
+ return nRet;
+ }
+
+ // Parse the body of the email message
+ msg.Parse(strMsg);
+ strMsg += _T("\r\n.\r\n");
+
+ // Send the body and expect a 250 OK reply.
+ nRet = SendCmd((LPTSTR)strMsg.c_str());
+ if (nRet != 250) RaiseError(nRet);
+
+ return (nRet == 250) ? 0:nRet;
+}
diff --git a/Src/Plugins/General/gen_crasher/feedback/smtp/Smtp.h b/Src/Plugins/General/gen_crasher/feedback/smtp/Smtp.h
new file mode 100644
index 00000000..50b852b6
--- /dev/null
+++ b/Src/Plugins/General/gen_crasher/feedback/smtp/Smtp.h
@@ -0,0 +1,245 @@
+// Smtp.h: interface for the CSmtp class.
+//
+// Written by Robert Simpson (robert@blackcastlesoft.com)
+// Created 11/1/2000
+// Version 1.7 -- Last Modified 06/18/2001
+// See smtp.cpp for details of this revision
+//////////////////////////////////////////////////////////////////////
+
+#if !defined(AFX_SMTP_H__F5ACA8FA_AF73_11D4_907D_0080C6F7C752__INCLUDED_)
+#define AFX_SMTP_H__F5ACA8FA_AF73_11D4_907D_0080C6F7C752__INCLUDED_
+
+#if _MSC_VER > 1000
+#pragma once
+#endif // _MSC_VER > 1000
+
+#pragma comment(lib,"wsock32.lib")
+
+#include <atlbase.h>
+#include <winsock.h>
+#include <string>
+#include "Base64.h"
+
+// Some ATL string conversion enhancements
+// ATL's string conversions allocate memory on the stack, which can
+// be undesirable if converting huge strings. These enhancements
+// provide for a pre-allocated memory block to be used as the
+// destination for the string conversion.
+#define _W2A(dst,src) AtlW2AHelper(dst,src,lstrlenW(src)+1)
+#define _A2W(dst,src) AtlA2WHelper(dst,src,lstrlenA(src)+1)
+
+typedef std::wstring StringW;
+typedef std::string StringA;
+
+#ifdef _UNICODE
+typedef StringW String;
+#define _W2T(dst,src) lstrcpyW(dst,src)
+#define _T2W(dst,src) lstrcpyW(dst,src)
+#define _T2A(dst,src) _W2A(dst,src)
+#define _A2T(dst,src) _A2W(dst,src)
+#else
+typedef StringA String;
+#define _W2T(dst,src) _W2A(dst,src)
+#define _T2W(dst,src) _A2W(dst,src)
+#define _T2A(dst,src) lstrcpyA(dst,src)
+#define _A2T(dst,src) lstrcpyA(dst,src)
+#endif
+
+// When the SMTP server responds to a command, this is the
+// maximum size of a response I expect back.
+#ifndef CMD_RESPONSE_SIZE
+#define CMD_RESPONSE_SIZE 1024
+#endif
+
+// The CSmtp::SendCmd() function will send blocks no larger than this value
+// Any outgoing data larger than this value will trigger an SmtpProgress()
+// event for all blocks sent.
+#ifndef CMD_BLOCK_SIZE
+#define CMD_BLOCK_SIZE 1024
+#endif
+
+// Default mime version is 1.0 of course
+#ifndef MIME_VERSION
+#define MIME_VERSION _T("1.0")
+#endif
+
+// This is the message that would appear in an e-mail client that doesn't support
+// multipart messages
+#ifndef MULTIPART_MESSAGE
+#define MULTIPART_MESSAGE _T("This is a multipart message in MIME format")
+#endif
+
+// Default message body encoding
+#ifndef MESSAGE_ENCODING
+#define MESSAGE_ENCODING _T("text/plain")
+#endif
+
+// Default character set
+#ifndef MESSAGE_CHARSET
+#define MESSAGE_CHARSET _T("iso-8859-1")
+#endif
+
+// Some forward declares
+class CSmtp;
+class CSmtpAddress;
+class CSmtpMessage;
+class CSmtpAttachment;
+class CSmtpMessageBody;
+class CSmtpMimePart;
+
+// These are the only 4 encoding methods currently supported
+typedef enum EncodingEnum
+{
+ encodeGuess,
+ encode7Bit,
+ encode8Bit,
+ encodeQuotedPrintable,
+ encodeBase64
+};
+
+// This code supports three types of mime-types, and can optionally guess a mime type
+// based on message content.
+typedef enum MimeTypeEnum
+{
+ mimeGuess,
+ mimeMixed,
+ mimeAlternative,
+ mimeRelated
+};
+
+// Attachments and message bodies inherit from this class
+// It allows each part of a multipart MIME message to have its own attributes
+class CSmtpMimePart
+{
+public:
+ String Encoding; // Content encoding. Leave blank to let the system discover it
+ String Charset; // Character set for text attachments
+ String ContentId; // Unique content ID, leave blank to let the system handle it
+ EncodingEnum TransferEncoding; // How to encode for transferring to the server
+};
+
+// This class represents a user's text name and corresponding email address
+class CSmtpAddress
+{
+public: // Constructors
+ CSmtpAddress(LPCTSTR pszAddress = NULL, LPCTSTR pszName = NULL);
+
+public: // Operators
+ const CSmtpAddress& operator=(LPCTSTR pszAddress);
+ const CSmtpAddress& operator=(const String& strAddress);
+
+public: // Member Variables
+ String Name;
+ String Address;
+};
+
+// This class represents a file attachment
+class CSmtpAttachment : public CSmtpMimePart
+{
+public: // Constructors
+ CSmtpAttachment(LPCTSTR pszFilename = NULL, LPCTSTR pszAltName = NULL, BOOL bIsInline = FALSE, LPCTSTR pszEncoding = NULL, LPCTSTR pszCharset = MESSAGE_CHARSET, EncodingEnum encode = encodeGuess);
+
+public: // Operators
+ const CSmtpAttachment& operator=(LPCTSTR pszFilename);
+ const CSmtpAttachment& operator=(const String& strFilename);
+
+public: // Member Variables
+ String FileName; // Fully-qualified path and filename of this attachment
+ String AltName; // Optional, an alternate name for the file to use when sending
+ BOOL Inline; // Is this an inline attachment?
+};
+
+// Multiple message body part support
+class CSmtpMessageBody : public CSmtpMimePart
+{
+public: // Constructors
+ CSmtpMessageBody(LPCTSTR pszBody = NULL, LPCTSTR pszEncoding = MESSAGE_ENCODING, LPCTSTR pszCharset = MESSAGE_CHARSET, EncodingEnum encode = encodeGuess);
+
+public: // Operators
+ const CSmtpMessageBody& operator=(LPCTSTR pszBody);
+ const CSmtpMessageBody& operator=(const String& strBody);
+
+public: // Member Variables
+ String Data; // Message body;
+};
+
+// This class represents a single message that can be sent via CSmtp
+class CSmtpMessage
+{
+public: // Constructors
+ CSmtpMessage();
+
+public: // Member Variables
+ CSmtpAddress Sender; // Who the message is from
+ CSmtpAddress Recipient; // The intended recipient
+ String Subject; // The message subject
+ CSimpleArray<CSmtpMessageBody> Message; // An array of message bodies
+ CSimpleArray<CSmtpAddress> CC; // Carbon Copy recipients
+ CSimpleArray<CSmtpAddress> BCC; // Blind Carbon Copy recipients
+ CSimpleArray<CSmtpAttachment> Attachments; // An array of attachments
+ CSimpleMap<String,String> Headers; // Optional headers to include in the message
+ SYSTEMTIME Timestamp; // Timestamp of the message
+ MimeTypeEnum MimeType; // Type of MIME message this is
+ String MessageId; // Optional message ID
+
+private: // Private Member Variables
+ int GMTOffset; // GMT timezone offset value
+
+public: // Public functions
+ void Parse(String& strDest);
+
+private: // Private functions to finalize the message headers & parse the message
+ EncodingEnum GuessEncoding(LPBYTE pByte, DWORD dwLen);
+ void EncodeMessage(EncodingEnum code, String& strMsg, String& strMethod, LPBYTE pByte = NULL, DWORD dwSize = 0);
+ void Make7Bit(String& strDest, String& strSrc);
+ void CommitHeaders();
+ void BreakMessage(String& strDest, String& strSrc, int nLength = 76);
+ void EncodeQuotedPrintable(String& strDest, String& strSrc);
+};
+
+// The main class for connecting to a SMTP server and sending mail.
+class CSmtp
+{
+public: // Constructors
+ CSmtp();
+ virtual ~CSmtp();
+
+public: // Member Variables. Feel free to modify these to change the system's behavior
+ BOOL m_bExtensions; // Use ESMTP extensions (TRUE)
+ DWORD m_dwCmdTimeout; // Timeout for issuing each command (30 seconds)
+ WORD m_wSmtpPort; // Port to communicate via SMTP (25)
+ String m_strUser; // Username for authentication
+ String m_strPass; // Password for authentication
+
+private: // Private Member Variables
+ SOCKET m_hSocket; // Socket being used to communicate to the SMTP server
+ String m_strResult; // String result from a SendCmd()
+ BOOL m_bConnected; // Connected to SMTP server
+ BOOL m_bUsingExtensions;// Whether this SMTP server uses ESMTP extensions
+
+public: // These represent the primary public functionality of this class
+ BOOL Connect(LPTSTR pszServer);
+ int SendMessage(CSmtpMessage& msg);
+ int SendMessage(CSmtpAddress& addrFrom, CSmtpAddress& addrTo, LPCTSTR pszSubject, LPTSTR pszMessage, LPVOID pvAttachments = NULL, DWORD dwAttachmentCount = 0);
+ int SendMessage(LPTSTR pszAddrFrom, LPTSTR pszAddrTo, LPTSTR pszSubject, LPTSTR pszMessage, LPVOID pvAttachments = NULL, DWORD dwAttachmentCount = 0);
+ void Close();
+
+public: // These represent the overridable methods for receiving events from this class
+ virtual int SmtpWarning(int nWarning, LPTSTR pszWarning);
+ virtual int SmtpError(int nCode, LPTSTR pszErr);
+ virtual void SmtpCommandResponse(LPTSTR pszCmd, int nResponse, LPTSTR pszResponse);
+ virtual BOOL SmtpProgress(LPSTR pszBuffer, DWORD dwSent, DWORD dwTotal);
+
+private: // These functions are used privately to conduct a SMTP session
+ int SendCmd(LPTSTR pszCmd);
+ int SendAuthentication();
+ int SendHello();
+ int SendQuitCmd();
+ int SendFrom(LPTSTR pszFrom);
+ int SendTo(LPTSTR pszTo);
+ int SendData(CSmtpMessage &msg);
+ int RaiseWarning(int nWarning);
+ int RaiseError(int nError);
+};
+
+#endif // !defined(AFX_SMTP_H__F5ACA8FA_AF73_11D4_907D_0080C6F7C752__INCLUDED_)
diff --git a/Src/Plugins/General/gen_crasher/feedback/version.rc2 b/Src/Plugins/General/gen_crasher/feedback/version.rc2
new file mode 100644
index 00000000..e896cb91
--- /dev/null
+++ b/Src/Plugins/General/gen_crasher/feedback/version.rc2
@@ -0,0 +1,39 @@
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Version
+//
+#include "..\..\..\..\Winamp/buildType.h"
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION WINAMP_PRODUCTVER
+ PRODUCTVERSION WINAMP_PRODUCTVER
+ FILEFLAGSMASK 0x17L
+#ifdef _DEBUG
+ FILEFLAGS 0x1L
+#else
+ FILEFLAGS 0x0L
+#endif
+ FILEOS 0x4L
+ FILETYPE 0x2L
+ FILESUBTYPE 0x0L
+BEGIN
+ BLOCK "StringFileInfo"
+ BEGIN
+ BLOCK "040904b0"
+ BEGIN
+ VALUE "CompanyName", "Winamp SA"
+ VALUE "FileDescription", "Winamp Error Reporter"
+ VALUE "FileVersion", STR_WINAMP_PRODUCTVER
+ VALUE "InternalName", "Winamp Error Reporter"
+ VALUE "LegalCopyright", "Copyright © 2005-2023 Winamp SA"
+ VALUE "LegalTrademarks", "Nullsoft and Winamp are trademarks of Winamp SA"
+ VALUE "OriginalFilename", "feedback.exe"
+ VALUE "ProductName", "Winamp"
+ VALUE "ProductVersion", STR_WINAMP_PRODUCTVER
+ END
+ END
+ BLOCK "VarFileInfo"
+ BEGIN
+ VALUE "Translation", 0x409, 1200
+ END
+END
diff --git a/Src/Plugins/General/gen_crasher/feedback/xzip/XZip.cpp b/Src/Plugins/General/gen_crasher/feedback/xzip/XZip.cpp
new file mode 100644
index 00000000..0e2ac6a3
--- /dev/null
+++ b/Src/Plugins/General/gen_crasher/feedback/xzip/XZip.cpp
@@ -0,0 +1,2915 @@
+// XZip.cpp Version 1.1
+//
+// Authors: Mark Adler et al. (see below)
+//
+// Modified by: Lucian Wischik
+// lu@wischik.com
+//
+// Version 1.0 - Turned C files into just a single CPP file
+// - Made them compile cleanly as C++ files
+// - Gave them simpler APIs
+// - Added the ability to zip/unzip directly in memory without
+// any intermediate files
+//
+// Modified by: Hans Dietrich
+// hdietrich2@hotmail.com
+//
+// Version 1.1: - Added Unicode support to CreateZip() and ZipAdd()
+// - Changed file names to avoid conflicts with Lucian's files
+//
+///////////////////////////////////////////////////////////////////////////////
+//
+// Lucian Wischik's comments:
+// --------------------------
+// THIS FILE is almost entirely based upon code by Info-ZIP.
+// It has been modified by Lucian Wischik.
+// The original code may be found at http://www.info-zip.org
+// The original copyright text follows.
+//
+///////////////////////////////////////////////////////////////////////////////
+//
+// Original authors' comments:
+// ---------------------------
+// This is version 2002-Feb-16 of the Info-ZIP copyright and license. The
+// definitive version of this document should be available at
+// ftp://ftp.info-zip.org/pub/infozip/license.html indefinitely.
+//
+// Copyright (c) 1990-2002 Info-ZIP. All rights reserved.
+//
+// For the purposes of this copyright and license, "Info-ZIP" is defined as
+// the following set of individuals:
+//
+// Mark Adler, John Bush, Karl Davis, Harald Denker, Jean-Michel Dubois,
+// Jean-loup Gailly, Hunter Goatley, Ian Gorman, Chris Herborth, Dirk Haase,
+// Greg Hartwig, Robert Heath, Jonathan Hudson, Paul Kienitz,
+// David Kirschbaum, Johnny Lee, Onno van der Linden, Igor Mandrichenko,
+// Steve P. Miller, Sergio Monesi, Keith Owens, George Petrov, Greg Roelofs,
+// Kai Uwe Rommel, Steve Salisbury, Dave Smith, Christian Spieler,
+// Antoine Verheijen, Paul von Behren, Rich Wales, Mike White
+//
+// This software is provided "as is", without warranty of any kind, express
+// or implied. In no event shall Info-ZIP or its contributors be held liable
+// for any direct, indirect, incidental, special or consequential damages
+// arising out of the use of or inability to use this software.
+//
+// Permission is granted to anyone to use this software for any purpose,
+// including commercial applications, and to alter it and redistribute it
+// freely, subject to the following restrictions:
+//
+// 1. Redistributions of source code must retain the above copyright notice,
+// definition, disclaimer, and this list of conditions.
+//
+// 2. Redistributions in binary form (compiled executables) must reproduce
+// the above copyright notice, definition, disclaimer, and this list of
+// conditions in documentation and/or other materials provided with the
+// distribution. The sole exception to this condition is redistribution
+// of a standard UnZipSFX binary as part of a self-extracting archive;
+// that is permitted without inclusion of this license, as long as the
+// normal UnZipSFX banner has not been removed from the binary or disabled.
+//
+// 3. Altered versions--including, but not limited to, ports to new
+// operating systems, existing ports with new graphical interfaces, and
+// dynamic, shared, or static library versions--must be plainly marked
+// as such and must not be misrepresented as being the original source.
+// Such altered versions also must not be misrepresented as being
+// Info-ZIP releases--including, but not limited to, labeling of the
+// altered versions with the names "Info-ZIP" (or any variation thereof,
+// including, but not limited to, different capitalizations),
+// "Pocket UnZip", "WiZ" or "MacZip" without the explicit permission of
+// Info-ZIP. Such altered versions are further prohibited from
+// misrepresentative use of the Zip-Bugs or Info-ZIP e-mail addresses or
+// of the Info-ZIP URL(s).
+//
+// 4. Info-ZIP retains the right to use the names "Info-ZIP", "Zip", "UnZip",
+// "UnZipSFX", "WiZ", "Pocket UnZip", "Pocket Zip", and "MacZip" for its
+// own source and binary releases.
+//
+///////////////////////////////////////////////////////////////////////////////
+
+#include <windows.h>
+#include <time.h>
+#include "xzip.h"
+
+
+typedef unsigned char uch; // unsigned 8-bit value
+typedef unsigned short ush; // unsigned 16-bit value
+typedef unsigned long ulg; // unsigned 32-bit value
+typedef size_t extent; // file size
+typedef unsigned Pos; // must be at least 32 bits
+typedef unsigned IPos; // A Pos is an index in the character window. Pos is used only for parameter passing
+
+#ifndef EOF
+#define EOF (-1)
+#endif
+
+
+// Error return values. The values 0..4 and 12..18 follow the conventions
+// of PKZIP. The values 4..10 are all assigned to "insufficient memory"
+// by PKZIP, so the codes 5..10 are used here for other purposes.
+#define ZE_MISS -1 // used by procname(), zipbare()
+#define ZE_OK 0 // success
+#define ZE_EOF 2 // unexpected end of zip file
+#define ZE_FORM 3 // zip file structure error
+#define ZE_MEM 4 // out of memory
+#define ZE_LOGIC 5 // internal logic error
+#define ZE_BIG 6 // entry too large to split
+#define ZE_NOTE 7 // invalid comment format
+#define ZE_TEST 8 // zip test (-T) failed or out of memory
+#define ZE_ABORT 9 // user interrupt or termination
+#define ZE_TEMP 10 // error using a temp file
+#define ZE_READ 11 // read or seek error
+#define ZE_NONE 12 // nothing to do
+#define ZE_NAME 13 // missing or empty zip file
+#define ZE_WRITE 14 // error writing to a file
+#define ZE_CREAT 15 // couldn't open to write
+#define ZE_PARMS 16 // bad command line
+#define ZE_OPEN 18 // could not open a specified file to read
+#define ZE_MAXERR 18 // the highest error number
+
+
+// internal file attribute
+#define UNKNOWN (-1)
+#define BINARY 0
+#define ASCII 1
+
+#define BEST -1 // Use best method (deflation or store)
+#define STORE 0 // Store method
+#define DEFLATE 8 // Deflation method
+
+#define CRCVAL_INITIAL 0L
+
+// MSDOS file or directory attributes
+#define MSDOS_HIDDEN_ATTR 0x02
+#define MSDOS_DIR_ATTR 0x10
+
+// Lengths of headers after signatures in bytes
+#define LOCHEAD 26
+#define CENHEAD 42
+#define ENDHEAD 18
+
+// Definitions for extra field handling:
+#define EB_HEADSIZE 4 /* length of a extra field block header */
+#define EB_LEN 2 /* offset of data length field in header */
+#define EB_UT_MINLEN 1 /* minimal UT field contains Flags byte */
+#define EB_UT_FLAGS 0 /* byte offset of Flags field */
+#define EB_UT_TIME1 1 /* byte offset of 1st time value */
+#define EB_UT_FL_MTIME (1 << 0) /* mtime present */
+#define EB_UT_FL_ATIME (1 << 1) /* atime present */
+#define EB_UT_FL_CTIME (1 << 2) /* ctime present */
+#define EB_UT_LEN(n) (EB_UT_MINLEN + 4 * (n))
+#define EB_L_UT_SIZE (EB_HEADSIZE + EB_UT_LEN(3))
+#define EB_C_UT_SIZE (EB_HEADSIZE + EB_UT_LEN(1))
+
+
+// Macros for writing machine integers to little-endian format
+#define PUTSH(a,f) {char _putsh_c=(char)((a)&0xff); wfunc(param,&_putsh_c,1); _putsh_c=(char)((a)>>8); wfunc(param,&_putsh_c,1);}
+#define PUTLG(a,f) {PUTSH((a) & 0xffff,(f)) PUTSH((a) >> 16,(f))}
+
+
+// -- Structure of a ZIP file --
+// Signatures for zip file information headers
+#define LOCSIG 0x04034b50L
+#define CENSIG 0x02014b50L
+#define ENDSIG 0x06054b50L
+#define EXTLOCSIG 0x08074b50L
+
+
+#define MIN_MATCH 3
+#define MAX_MATCH 258
+// The minimum and maximum match lengths
+
+
+#define WSIZE (0x8000)
+// Maximum window size = 32K. If you are really short of memory, compile
+// with a smaller WSIZE but this reduces the compression ratio for files
+// of size > WSIZE. WSIZE must be a power of two in the current implementation.
+//
+
+#define MIN_LOOKAHEAD (MAX_MATCH+MIN_MATCH+1)
+// Minimum amount of lookahead, except at the end of the input file.
+// See deflate.c for comments about the MIN_MATCH+1.
+//
+
+#define MAX_DIST (WSIZE-MIN_LOOKAHEAD)
+// In order to simplify the code, particularly on 16 bit machines, match
+// distances are limited to MAX_DIST instead of WSIZE.
+//
+
+
+
+
+
+// ===========================================================================
+// Constants
+//
+
+#define MAX_BITS 15
+// All codes must not exceed MAX_BITS bits
+
+#define MAX_BL_BITS 7
+// Bit length codes must not exceed MAX_BL_BITS bits
+
+#define LENGTH_CODES 29
+// number of length codes, not counting the special END_BLOCK code
+
+#define LITERALS 256
+// number of literal bytes 0..255
+
+#define END_BLOCK 256
+// end of block literal code
+
+#define L_CODES (LITERALS+1+LENGTH_CODES)
+// number of Literal or Length codes, including the END_BLOCK code
+
+#define D_CODES 30
+// number of distance codes
+
+#define BL_CODES 19
+// number of codes used to transfer the bit lengths
+
+
+#define STORED_BLOCK 0
+#define STATIC_TREES 1
+#define DYN_TREES 2
+// The three kinds of block type
+
+#define LIT_BUFSIZE 0x8000
+#define DIST_BUFSIZE LIT_BUFSIZE
+// Sizes of match buffers for literals/lengths and distances. There are
+// 4 reasons for limiting LIT_BUFSIZE to 64K:
+// - frequencies can be kept in 16 bit counters
+// - if compression is not successful for the first block, all input data is
+// still in the window so we can still emit a stored block even when input
+// comes from standard input. (This can also be done for all blocks if
+// LIT_BUFSIZE is not greater than 32K.)
+// - if compression is not successful for a file smaller than 64K, we can
+// even emit a stored file instead of a stored block (saving 5 bytes).
+// - creating new Huffman trees less frequently may not provide fast
+// adaptation to changes in the input data statistics. (Take for
+// example a binary file with poorly compressible code followed by
+// a highly compressible string table.) Smaller buffer sizes give
+// fast adaptation but have of course the overhead of transmitting trees
+// more frequently.
+// - I can't count above 4
+// The current code is general and allows DIST_BUFSIZE < LIT_BUFSIZE (to save
+// memory at the expense of compression). Some optimizations would be possible
+// if we rely on DIST_BUFSIZE == LIT_BUFSIZE.
+//
+
+#define REP_3_6 16
+// repeat previous bit length 3-6 times (2 bits of repeat count)
+
+#define REPZ_3_10 17
+// repeat a zero length 3-10 times (3 bits of repeat count)
+
+#define REPZ_11_138 18
+// repeat a zero length 11-138 times (7 bits of repeat count)
+
+#define HEAP_SIZE (2*L_CODES+1)
+// maximum heap size
+
+
+// ===========================================================================
+// Local data used by the "bit string" routines.
+//
+
+#define Buf_size (8 * 2*sizeof(char))
+// Number of bits used within bi_buf. (bi_buf may be implemented on
+// more than 16 bits on some systems.)
+
+// Output a 16 bit value to the bit stream, lower (oldest) byte first
+#define PUTSHORT(state,w) \
+{ if (state.bs.out_offset >= state.bs.out_size-1) \
+ state.flush_outbuf(state.param,state.bs.out_buf, &state.bs.out_offset); \
+ state.bs.out_buf[state.bs.out_offset++] = (char) ((w) & 0xff); \
+ state.bs.out_buf[state.bs.out_offset++] = (char) ((ush)(w) >> 8); \
+}
+
+#define PUTBYTE(state,b) \
+{ if (state.bs.out_offset >= state.bs.out_size) \
+ state.flush_outbuf(state.param,state.bs.out_buf, &state.bs.out_offset); \
+ state.bs.out_buf[state.bs.out_offset++] = (char) (b); \
+}
+
+// DEFLATE.CPP HEADER
+
+#define HASH_BITS 15
+// For portability to 16 bit machines, do not use values above 15.
+
+#define HASH_SIZE (unsigned)(1<<HASH_BITS)
+#define HASH_MASK (HASH_SIZE-1)
+#define WMASK (WSIZE-1)
+// HASH_SIZE and WSIZE must be powers of two
+
+#define NIL 0
+// Tail of hash chains
+
+#define FAST 4
+#define SLOW 2
+// speed options for the general purpose bit flag
+
+#define TOO_FAR 4096
+// Matches of length 3 are discarded if their distance exceeds TOO_FAR
+
+
+
+#define EQUAL 0
+// result of memcmp for equal strings
+
+
+// ===========================================================================
+// Local data used by the "longest match" routines.
+
+#define H_SHIFT ((HASH_BITS+MIN_MATCH-1)/MIN_MATCH)
+// Number of bits by which ins_h and del_h must be shifted at each
+// input step. It must be such that after MIN_MATCH steps, the oldest
+// byte no longer takes part in the hash key, that is:
+// H_SHIFT * MIN_MATCH >= HASH_BITS
+
+#define max_insert_length max_lazy_match
+// Insert new strings in the hash table only if the match length
+// is not greater than this length. This saves time but degrades compression.
+// max_insert_length is used only for compression levels <= 3.
+
+
+
+const int extra_lbits[LENGTH_CODES] // extra bits for each length code
+ = {0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0};
+
+const int extra_dbits[D_CODES] // extra bits for each distance code
+ = {0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13};
+
+const int extra_blbits[BL_CODES]// extra bits for each bit length code
+ = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,3,7};
+
+const uch bl_order[BL_CODES] = {16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15};
+// The lengths of the bit length codes are sent in order of decreasing
+// probability, to avoid transmitting the lengths for unused bit length codes.
+
+
+typedef struct config {
+ ush good_length; // reduce lazy search above this match length
+ ush max_lazy; // do not perform lazy search above this match length
+ ush nice_length; // quit search above this match length
+ ush max_chain;
+} config;
+
+// Values for max_lazy_match, good_match, nice_match and max_chain_length,
+// depending on the desired pack level (0..9). The values given below have
+// been tuned to exclude worst case performance for pathological files.
+// Better values may be found for specific files.
+//
+
+const config configuration_table[10] = {
+// good lazy nice chain
+ {0, 0, 0, 0}, // 0 store only
+ {4, 4, 8, 4}, // 1 maximum speed, no lazy matches
+ {4, 5, 16, 8}, // 2
+ {4, 6, 32, 32}, // 3
+ {4, 4, 16, 16}, // 4 lazy matches */
+ {8, 16, 32, 32}, // 5
+ {8, 16, 128, 128}, // 6
+ {8, 32, 128, 256}, // 7
+ {32, 128, 258, 1024}, // 8
+ {32, 258, 258, 4096}};// 9 maximum compression */
+
+// Note: the deflate() code requires max_lazy >= MIN_MATCH and max_chain >= 4
+// For deflate_fast() (levels <= 3) good is ignored and lazy has a different meaning.
+
+
+
+
+
+// Data structure describing a single value and its code string.
+typedef struct ct_data {
+ union {
+ ush freq; // frequency count
+ ush code; // bit string
+ } fc;
+ union {
+ ush dad; // father node in Huffman tree
+ ush len; // length of bit string
+ } dl;
+} ct_data;
+
+typedef struct tree_desc {
+ ct_data *dyn_tree; // the dynamic tree
+ ct_data *static_tree; // corresponding static tree or NULL
+ const int *extra_bits; // extra bits for each code or NULL
+ int extra_base; // base index for extra_bits
+ int elems; // max number of elements in the tree
+ int max_length; // max bit length for the codes
+ int max_code; // largest code with non zero frequency
+} tree_desc;
+
+
+
+
+class TTreeState
+{ public:
+ TTreeState();
+
+ ct_data dyn_ltree[HEAP_SIZE]; // literal and length tree
+ ct_data dyn_dtree[2*D_CODES+1]; // distance tree
+ ct_data static_ltree[L_CODES+2]; // the static literal tree...
+ // ... Since the bit lengths are imposed, there is no need for the L_CODES
+ // extra codes used during heap construction. However the codes 286 and 287
+ // are needed to build a canonical tree (see ct_init below).
+ ct_data static_dtree[D_CODES]; // the static distance tree...
+ // ... (Actually a trivial tree since all codes use 5 bits.)
+ ct_data bl_tree[2*BL_CODES+1]; // Huffman tree for the bit lengths
+
+ tree_desc l_desc;
+ tree_desc d_desc;
+ tree_desc bl_desc;
+
+ ush bl_count[MAX_BITS+1]; // number of codes at each bit length for an optimal tree
+
+ int heap[2*L_CODES+1]; // heap used to build the Huffman trees
+ int heap_len; // number of elements in the heap
+ int heap_max; // element of largest frequency
+ // The sons of heap[n] are heap[2*n] and heap[2*n+1]. heap[0] is not used.
+ // The same heap array is used to build all trees.
+
+ uch depth[2*L_CODES+1];
+ // Depth of each subtree used as tie breaker for trees of equal frequency
+
+ uch length_code[MAX_MATCH-MIN_MATCH+1];
+ // length code for each normalized match length (0 == MIN_MATCH)
+
+ uch dist_code[512];
+ // distance codes. The first 256 values correspond to the distances
+ // 3 .. 258, the last 256 values correspond to the top 8 bits of
+ // the 15 bit distances.
+
+ int base_length[LENGTH_CODES];
+ // First normalized length for each code (0 = MIN_MATCH)
+
+ int base_dist[D_CODES];
+ // First normalized distance for each code (0 = distance of 1)
+
+ uch far l_buf[LIT_BUFSIZE]; // buffer for literals/lengths
+ ush far d_buf[DIST_BUFSIZE]; // buffer for distances
+
+ uch flag_buf[(LIT_BUFSIZE/8)];
+ // flag_buf is a bit array distinguishing literals from lengths in
+ // l_buf, and thus indicating the presence or absence of a distance.
+
+ unsigned last_lit; // running index in l_buf
+ unsigned last_dist; // running index in d_buf
+ unsigned last_flags; // running index in flag_buf
+ uch flags; // current flags not yet saved in flag_buf
+ uch flag_bit; // current bit used in flags
+ // bits are filled in flags starting at bit 0 (least significant).
+ // Note: these flags are overkill in the current code since we don't
+ // take advantage of DIST_BUFSIZE == LIT_BUFSIZE.
+
+ ulg opt_len; // bit length of current block with optimal trees
+ ulg static_len; // bit length of current block with static trees
+
+ ulg cmpr_bytelen; // total byte length of compressed file
+ ulg cmpr_len_bits; // number of bits past 'cmpr_bytelen'
+
+ ulg input_len; // total byte length of input file
+ // input_len is for debugging only since we can get it by other means.
+
+ ush *file_type; // pointer to UNKNOWN, BINARY or ASCII
+// int *file_method; // pointer to DEFLATE or STORE
+};
+
+TTreeState::TTreeState()
+{ tree_desc a = {dyn_ltree, static_ltree, extra_lbits, LITERALS+1, L_CODES, MAX_BITS, 0}; l_desc = a;
+ tree_desc b = {dyn_dtree, static_dtree, extra_dbits, 0, D_CODES, MAX_BITS, 0}; d_desc = b;
+ tree_desc c = {bl_tree, NULL, extra_blbits, 0, BL_CODES, MAX_BL_BITS, 0}; bl_desc = c;
+ last_lit=0;
+ last_dist=0;
+ last_flags=0;
+}
+
+
+
+class TBitState
+{ public:
+
+ int flush_flg;
+ //
+ unsigned bi_buf;
+ // Output buffer. bits are inserted starting at the bottom (least significant
+ // bits). The width of bi_buf must be at least 16 bits.
+ int bi_valid;
+ // Number of valid bits in bi_buf. All bits above the last valid bit
+ // are always zero.
+ char *out_buf;
+ // Current output buffer.
+ unsigned out_offset;
+ // Current offset in output buffer.
+ // On 16 bit machines, the buffer is limited to 64K.
+ unsigned out_size;
+ // Size of current output buffer
+ ulg bits_sent; // bit length of the compressed data only needed for debugging???
+};
+
+
+
+
+
+
+
+class TDeflateState
+{ public:
+ TDeflateState() {window_size=0;}
+
+ uch window[2L*WSIZE];
+ // Sliding window. Input bytes are read into the second half of the window,
+ // and move to the first half later to keep a dictionary of at least WSIZE
+ // bytes. With this organization, matches are limited to a distance of
+ // WSIZE-MAX_MATCH bytes, but this ensures that IO is always
+ // performed with a length multiple of the block size. Also, it limits
+ // the window size to 64K, which is quite useful on MSDOS.
+ // To do: limit the window size to WSIZE+CBSZ if SMALL_MEM (the code would
+ // be less efficient since the data would have to be copied WSIZE/CBSZ times)
+ Pos prev[WSIZE];
+ // Link to older string with same hash index. To limit the size of this
+ // array to 64K, this link is maintained only for the last 32K strings.
+ // An index in this array is thus a window index modulo 32K.
+ Pos head[HASH_SIZE];
+ // Heads of the hash chains or NIL. If your compiler thinks that
+ // HASH_SIZE is a dynamic value, recompile with -DDYN_ALLOC.
+
+ ulg window_size;
+ // window size, 2*WSIZE except for MMAP or BIG_MEM, where it is the
+ // input file length plus MIN_LOOKAHEAD.
+
+ long block_start;
+ // window position at the beginning of the current output block. Gets
+ // negative when the window is moved backwards.
+
+ int sliding;
+ // Set to false when the input file is already in memory
+
+ unsigned ins_h; // hash index of string to be inserted
+
+ unsigned int prev_length;
+ // Length of the best match at previous step. Matches not greater than this
+ // are discarded. This is used in the lazy match evaluation.
+
+ unsigned strstart; // start of string to insert
+ unsigned match_start; // start of matching string
+ int eofile; // flag set at end of input file
+ unsigned lookahead; // number of valid bytes ahead in window
+
+ unsigned max_chain_length;
+ // To speed up deflation, hash chains are never searched beyond this length.
+ // A higher limit improves compression ratio but degrades the speed.
+
+ unsigned int max_lazy_match;
+ // Attempt to find a better match only when the current match is strictly
+ // smaller than this value. This mechanism is used only for compression
+ // levels >= 4.
+
+ unsigned good_match;
+ // Use a faster search when the previous match is longer than this
+
+ int nice_match; // Stop searching when current match exceeds this
+};
+
+
+typedef struct iztimes {
+ time_t atime,mtime,ctime;
+} iztimes; // access, modify, create times
+
+typedef struct zlist {
+ ush vem, ver, flg, how; // See central header in zipfile.c for what vem..off are
+ ulg tim, crc, siz, len;
+ extent nam, ext, cext, com; // offset of ext must be >= LOCHEAD
+ ush dsk, att, lflg; // offset of lflg must be >= LOCHEAD
+ ulg atx, off;
+ char name[MAX_PATH]; // File name in zip file
+ char *extra; // Extra field (set only if ext != 0)
+ char *cextra; // Extra in central (set only if cext != 0)
+ char *comment; // Comment (set only if com != 0)
+ char iname[MAX_PATH]; // Internal file name after cleanup
+ char zname[MAX_PATH]; // External version of internal name
+ int mark; // Marker for files to operate on
+ int trash; // Marker for files to delete
+ int dosflag; // Set to force MSDOS file attributes
+ struct zlist far *nxt; // Pointer to next header in list
+} TZipFileInfo;
+
+
+class TState;
+typedef unsigned (*READFUNC)(TState &state, char *buf,unsigned size);
+typedef unsigned (*FLUSHFUNC)(void *param, const char *buf, unsigned *size);
+typedef unsigned (*WRITEFUNC)(void *param, const char *buf, unsigned size);
+class TState
+{ public: TState() {err=0;}
+ //
+ void *param;
+ int level; bool seekable;
+ READFUNC readfunc; FLUSHFUNC flush_outbuf;
+ TTreeState ts; TBitState bs; TDeflateState ds;
+ const char *err;
+};
+
+
+
+
+
+
+
+
+
+void Assert(TState &state,bool cond, const char *msg)
+{ if (cond) return;
+ state.err=msg;
+}
+void __cdecl Trace(const char *x, ...) {va_list paramList; va_start(paramList, x); paramList; va_end(paramList);}
+void __cdecl Tracec(bool ,const char *x, ...) {va_list paramList; va_start(paramList, x); paramList; va_end(paramList);}
+
+
+
+// ===========================================================================
+// Local (static) routines in this file.
+//
+
+void init_block (TState &);
+void pqdownheap (TState &,ct_data *tree, int k);
+void gen_bitlen (TState &,tree_desc *desc);
+void gen_codes (TState &state,ct_data *tree, int max_code);
+void build_tree (TState &,tree_desc *desc);
+void scan_tree (TState &,ct_data *tree, int max_code);
+void send_tree (TState &state,ct_data *tree, int max_code);
+int build_bl_tree (TState &);
+void send_all_trees (TState &state,int lcodes, int dcodes, int blcodes);
+void compress_block (TState &state,ct_data *ltree, ct_data *dtree);
+void set_file_type (TState &);
+void send_bits (TState &state, int value, int length);
+unsigned bi_reverse (unsigned code, int len);
+void bi_windup (TState &state);
+void copy_block (TState &state,char *buf, unsigned len, int header);
+
+
+#define send_code(state, c, tree) send_bits(state, tree[c].fc.code, tree[c].dl.len)
+// Send a code of the given tree. c and tree must not have side effects
+
+// alternatively...
+//#define send_code(state, c, tree)
+// { if (state.verbose>1) fprintf(stderr,"\ncd %3d ",(c));
+// send_bits(state, tree[c].fc.code, tree[c].dl.len); }
+
+#define d_code(dist) ((dist) < 256 ? state.ts.dist_code[dist] : state.ts.dist_code[256+((dist)>>7)])
+// Mapping from a distance to a distance code. dist is the distance - 1 and
+// must not have side effects. dist_code[256] and dist_code[257] are never used.
+
+#define Max(a,b) (a >= b ? a : b)
+/* the arguments must not have side effects */
+
+/* ===========================================================================
+ * Allocate the match buffer, initialize the various tables and save the
+ * location of the internal file attribute (ascii/binary) and method
+ * (DEFLATE/STORE).
+ */
+void ct_init(TState &state, ush *attr)
+{
+ int n; /* iterates over tree elements */
+ int bits; /* bit counter */
+ int length; /* length value */
+ int code; /* code value */
+ int dist; /* distance index */
+
+ state.ts.file_type = attr;
+ //state.ts.file_method = method;
+ state.ts.cmpr_bytelen = state.ts.cmpr_len_bits = 0L;
+ state.ts.input_len = 0L;
+
+ if (state.ts.static_dtree[0].dl.len != 0) return; /* ct_init already called */
+
+ /* Initialize the mapping length (0..255) -> length code (0..28) */
+ length = 0;
+ for (code = 0; code < LENGTH_CODES-1; code++) {
+ state.ts.base_length[code] = length;
+ for (n = 0; n < (1<<extra_lbits[code]); n++) {
+ state.ts.length_code[length++] = (uch)code;
+ }
+ }
+ Assert(state,length == 256, "ct_init: length != 256");
+ /* Note that the length 255 (match length 258) can be represented
+ * in two different ways: code 284 + 5 bits or code 285, so we
+ * overwrite length_code[255] to use the best encoding:
+ */
+ state.ts.length_code[length-1] = (uch)code;
+
+ /* Initialize the mapping dist (0..32K) -> dist code (0..29) */
+ dist = 0;
+ for (code = 0 ; code < 16; code++) {
+ state.ts.base_dist[code] = dist;
+ for (n = 0; n < (1<<extra_dbits[code]); n++) {
+ state.ts.dist_code[dist++] = (uch)code;
+ }
+ }
+ Assert(state,dist == 256, "ct_init: dist != 256");
+ dist >>= 7; /* from now on, all distances are divided by 128 */
+ for ( ; code < D_CODES; code++) {
+ state.ts.base_dist[code] = dist << 7;
+ for (n = 0; n < (1<<(extra_dbits[code]-7)); n++) {
+ state.ts.dist_code[256 + dist++] = (uch)code;
+ }
+ }
+ Assert(state,dist == 256, "ct_init: 256+dist != 512");
+
+ /* Construct the codes of the static literal tree */
+ for (bits = 0; bits <= MAX_BITS; bits++) state.ts.bl_count[bits] = 0;
+ n = 0;
+ while (n <= 143) state.ts.static_ltree[n++].dl.len = 8, state.ts.bl_count[8]++;
+ while (n <= 255) state.ts.static_ltree[n++].dl.len = 9, state.ts.bl_count[9]++;
+ while (n <= 279) state.ts.static_ltree[n++].dl.len = 7, state.ts.bl_count[7]++;
+ while (n <= 287) state.ts.static_ltree[n++].dl.len = 8, state.ts.bl_count[8]++;
+ /* fc.codes 286 and 287 do not exist, but we must include them in the
+ * tree construction to get a canonical Huffman tree (longest code
+ * all ones)
+ */
+ gen_codes(state,(ct_data *)state.ts.static_ltree, L_CODES+1);
+
+ /* The static distance tree is trivial: */
+ for (n = 0; n < D_CODES; n++) {
+ state.ts.static_dtree[n].dl.len = 5;
+ state.ts.static_dtree[n].fc.code = (ush)bi_reverse(n, 5);
+ }
+
+ /* Initialize the first block of the first file: */
+ init_block(state);
+}
+
+/* ===========================================================================
+ * Initialize a new block.
+ */
+void init_block(TState &state)
+{
+ int n; /* iterates over tree elements */
+
+ /* Initialize the trees. */
+ for (n = 0; n < L_CODES; n++) state.ts.dyn_ltree[n].fc.freq = 0;
+ for (n = 0; n < D_CODES; n++) state.ts.dyn_dtree[n].fc.freq = 0;
+ for (n = 0; n < BL_CODES; n++) state.ts.bl_tree[n].fc.freq = 0;
+
+ state.ts.dyn_ltree[END_BLOCK].fc.freq = 1;
+ state.ts.opt_len = state.ts.static_len = 0L;
+ state.ts.last_lit = state.ts.last_dist = state.ts.last_flags = 0;
+ state.ts.flags = 0; state.ts.flag_bit = 1;
+}
+
+#define SMALLEST 1
+/* Index within the heap array of least frequent node in the Huffman tree */
+
+
+/* ===========================================================================
+ * Remove the smallest element from the heap and recreate the heap with
+ * one less element. Updates heap and heap_len.
+ */
+#define pqremove(tree, top) \
+{\
+ top = state.ts.heap[SMALLEST]; \
+ state.ts.heap[SMALLEST] = state.ts.heap[state.ts.heap_len--]; \
+ pqdownheap(state,tree, SMALLEST); \
+}
+
+/* ===========================================================================
+ * Compares to subtrees, using the tree depth as tie breaker when
+ * the subtrees have equal frequency. This minimizes the worst case length.
+ */
+#define smaller(tree, n, m) \
+ (tree[n].fc.freq < tree[m].fc.freq || \
+ (tree[n].fc.freq == tree[m].fc.freq && state.ts.depth[n] <= state.ts.depth[m]))
+
+/* ===========================================================================
+ * Restore the heap property by moving down the tree starting at node k,
+ * exchanging a node with the smallest of its two sons if necessary, stopping
+ * when the heap property is re-established (each father smaller than its
+ * two sons).
+ */
+void pqdownheap(TState &state,ct_data *tree, int k)
+{
+ int v = state.ts.heap[k];
+ int j = k << 1; /* left son of k */
+ int htemp; /* required because of bug in SASC compiler */
+
+ while (j <= state.ts.heap_len) {
+ /* Set j to the smallest of the two sons: */
+ if (j < state.ts.heap_len && smaller(tree, state.ts.heap[j+1], state.ts.heap[j])) j++;
+
+ /* Exit if v is smaller than both sons */
+ htemp = state.ts.heap[j];
+ if (smaller(tree, v, htemp)) break;
+
+ /* Exchange v with the smallest son */
+ state.ts.heap[k] = htemp;
+ k = j;
+
+ /* And continue down the tree, setting j to the left son of k */
+ j <<= 1;
+ }
+ state.ts.heap[k] = v;
+}
+
+/* ===========================================================================
+ * Compute the optimal bit lengths for a tree and update the total bit length
+ * for the current block.
+ * IN assertion: the fields freq and dad are set, heap[heap_max] and
+ * above are the tree nodes sorted by increasing frequency.
+ * OUT assertions: the field len is set to the optimal bit length, the
+ * array bl_count contains the frequencies for each bit length.
+ * The length opt_len is updated; static_len is also updated if stree is
+ * not null.
+ */
+void gen_bitlen(TState &state,tree_desc *desc)
+{
+ ct_data *tree = desc->dyn_tree;
+ const int *extra = desc->extra_bits;
+ int base = desc->extra_base;
+ int max_code = desc->max_code;
+ int max_length = desc->max_length;
+ ct_data *stree = desc->static_tree;
+ int h; /* heap index */
+ int n, m; /* iterate over the tree elements */
+ int bits; /* bit length */
+ int xbits; /* extra bits */
+ ush f; /* frequency */
+ int overflow = 0; /* number of elements with bit length too large */
+
+ for (bits = 0; bits <= MAX_BITS; bits++) state.ts.bl_count[bits] = 0;
+
+ /* In a first pass, compute the optimal bit lengths (which may
+ * overflow in the case of the bit length tree).
+ */
+ tree[state.ts.heap[state.ts.heap_max]].dl.len = 0; /* root of the heap */
+
+ for (h = state.ts.heap_max+1; h < HEAP_SIZE; h++) {
+ n = state.ts.heap[h];
+ bits = tree[tree[n].dl.dad].dl.len + 1;
+ if (bits > max_length) bits = max_length, overflow++;
+ tree[n].dl.len = (ush)bits;
+ /* We overwrite tree[n].dl.dad which is no longer needed */
+
+ if (n > max_code) continue; /* not a leaf node */
+
+ state.ts.bl_count[bits]++;
+ xbits = 0;
+ if (n >= base) xbits = extra[n-base];
+ f = tree[n].fc.freq;
+ state.ts.opt_len += (ulg)f * (bits + xbits);
+ if (stree) state.ts.static_len += (ulg)f * (stree[n].dl.len + xbits);
+ }
+ if (overflow == 0) return;
+
+ Trace("\nbit length overflow\n");
+ /* This happens for example on obj2 and pic of the Calgary corpus */
+
+ /* Find the first bit length which could increase: */
+ do {
+ bits = max_length-1;
+ while (state.ts.bl_count[bits] == 0) bits--;
+ state.ts.bl_count[bits]--; /* move one leaf down the tree */
+ state.ts.bl_count[bits+1] += (ush)2; /* move one overflow item as its brother */
+ state.ts.bl_count[max_length]--;
+ /* The brother of the overflow item also moves one step up,
+ * but this does not affect bl_count[max_length]
+ */
+ overflow -= 2;
+ } while (overflow > 0);
+
+ /* Now recompute all bit lengths, scanning in increasing frequency.
+ * h is still equal to HEAP_SIZE. (It is simpler to reconstruct all
+ * lengths instead of fixing only the wrong ones. This idea is taken
+ * from 'ar' written by Haruhiko Okumura.)
+ */
+ for (bits = max_length; bits != 0; bits--) {
+ n = state.ts.bl_count[bits];
+ while (n != 0) {
+ m = state.ts.heap[--h];
+ if (m > max_code) continue;
+ if (tree[m].dl.len != (ush)bits) {
+ Trace("code %d bits %d->%d\n", m, tree[m].dl.len, bits);
+ state.ts.opt_len += ((long)bits-(long)tree[m].dl.len)*(long)tree[m].fc.freq;
+ tree[m].dl.len = (ush)bits;
+ }
+ n--;
+ }
+ }
+}
+
+/* ===========================================================================
+ * Generate the codes for a given tree and bit counts (which need not be
+ * optimal).
+ * IN assertion: the array bl_count contains the bit length statistics for
+ * the given tree and the field len is set for all tree elements.
+ * OUT assertion: the field code is set for all tree elements of non
+ * zero code length.
+ */
+void gen_codes (TState &state, ct_data *tree, int max_code)
+{
+ ush next_code[MAX_BITS+1]; /* next code value for each bit length */
+ ush code = 0; /* running code value */
+ int bits; /* bit index */
+ int n; /* code index */
+
+ /* The distribution counts are first used to generate the code values
+ * without bit reversal.
+ */
+ for (bits = 1; bits <= MAX_BITS; bits++) {
+ next_code[bits] = code = (ush)((code + state.ts.bl_count[bits-1]) << 1);
+ }
+ /* Check that the bit counts in bl_count are consistent. The last code
+ * must be all ones.
+ */
+ Assert(state,code + state.ts.bl_count[MAX_BITS]-1 == (1<< ((ush) MAX_BITS)) - 1,
+ "inconsistent bit counts");
+ Trace("\ngen_codes: max_code %d ", max_code);
+
+ for (n = 0; n <= max_code; n++) {
+ int len = tree[n].dl.len;
+ if (len == 0) continue;
+ /* Now reverse the bits */
+ tree[n].fc.code = (ush)bi_reverse(next_code[len]++, len);
+
+ //Tracec(tree != state.ts.static_ltree, "\nn %3d %c l %2d c %4x (%x) ", n, (isgraph(n) ? n : ' '), len, tree[n].fc.code, next_code[len]-1);
+ }
+}
+
+/* ===========================================================================
+ * Construct one Huffman tree and assigns the code bit strings and lengths.
+ * Update the total bit length for the current block.
+ * IN assertion: the field freq is set for all tree elements.
+ * OUT assertions: the fields len and code are set to the optimal bit length
+ * and corresponding code. The length opt_len is updated; static_len is
+ * also updated if stree is not null. The field max_code is set.
+ */
+void build_tree(TState &state,tree_desc *desc)
+{
+ ct_data *tree = desc->dyn_tree;
+ ct_data *stree = desc->static_tree;
+ int elems = desc->elems;
+ int n, m; /* iterate over heap elements */
+ int max_code = -1; /* largest code with non zero frequency */
+ int node = elems; /* next internal node of the tree */
+
+ /* Construct the initial heap, with least frequent element in
+ * heap[SMALLEST]. The sons of heap[n] are heap[2*n] and heap[2*n+1].
+ * heap[0] is not used.
+ */
+ state.ts.heap_len = 0, state.ts.heap_max = HEAP_SIZE;
+
+ for (n = 0; n < elems; n++) {
+ if (tree[n].fc.freq != 0) {
+ state.ts.heap[++state.ts.heap_len] = max_code = n;
+ state.ts.depth[n] = 0;
+ } else {
+ tree[n].dl.len = 0;
+ }
+ }
+
+ /* The pkzip format requires that at least one distance code exists,
+ * and that at least one bit should be sent even if there is only one
+ * possible code. So to avoid special checks later on we force at least
+ * two codes of non zero frequency.
+ */
+ while (state.ts.heap_len < 2) {
+ int newcp = state.ts.heap[++state.ts.heap_len] = (max_code < 2 ? ++max_code : 0);
+ tree[newcp].fc.freq = 1;
+ state.ts.depth[newcp] = 0;
+ state.ts.opt_len--; if (stree) state.ts.static_len -= stree[newcp].dl.len;
+ /* new is 0 or 1 so it does not have extra bits */
+ }
+ desc->max_code = max_code;
+
+ /* The elements heap[heap_len/2+1 .. heap_len] are leaves of the tree,
+ * establish sub-heaps of increasing lengths:
+ */
+ for (n = state.ts.heap_len/2; n >= 1; n--) pqdownheap(state,tree, n);
+
+ /* Construct the Huffman tree by repeatedly combining the least two
+ * frequent nodes.
+ */
+ do {
+ pqremove(tree, n); /* n = node of least frequency */
+ m = state.ts.heap[SMALLEST]; /* m = node of next least frequency */
+
+ state.ts.heap[--state.ts.heap_max] = n; /* keep the nodes sorted by frequency */
+ state.ts.heap[--state.ts.heap_max] = m;
+
+ /* Create a new node father of n and m */
+ tree[node].fc.freq = (ush)(tree[n].fc.freq + tree[m].fc.freq);
+ state.ts.depth[node] = (uch) (Max(state.ts.depth[n], state.ts.depth[m]) + 1);
+ tree[n].dl.dad = tree[m].dl.dad = (ush)node;
+ /* and insert the new node in the heap */
+ state.ts.heap[SMALLEST] = node++;
+ pqdownheap(state,tree, SMALLEST);
+
+ } while (state.ts.heap_len >= 2);
+
+ state.ts.heap[--state.ts.heap_max] = state.ts.heap[SMALLEST];
+
+ /* At this point, the fields freq and dad are set. We can now
+ * generate the bit lengths.
+ */
+ gen_bitlen(state,(tree_desc *)desc);
+
+ /* The field len is now set, we can generate the bit codes */
+ gen_codes (state,(ct_data *)tree, max_code);
+}
+
+/* ===========================================================================
+ * Scan a literal or distance tree to determine the frequencies of the codes
+ * in the bit length tree. Updates opt_len to take into account the repeat
+ * counts. (The contribution of the bit length codes will be added later
+ * during the construction of bl_tree.)
+ */
+void scan_tree (TState &state,ct_data *tree, int max_code)
+{
+ int n; /* iterates over all tree elements */
+ int prevlen = -1; /* last emitted length */
+ int curlen; /* length of current code */
+ int nextlen = tree[0].dl.len; /* length of next code */
+ int count = 0; /* repeat count of the current code */
+ int max_count = 7; /* max repeat count */
+ int min_count = 4; /* min repeat count */
+
+ if (nextlen == 0) max_count = 138, min_count = 3;
+ tree[max_code+1].dl.len = (ush)-1; /* guard */
+
+ for (n = 0; n <= max_code; n++) {
+ curlen = nextlen; nextlen = tree[n+1].dl.len;
+ if (++count < max_count && curlen == nextlen) {
+ continue;
+ } else if (count < min_count) {
+ state.ts.bl_tree[curlen].fc.freq = (ush)(state.ts.bl_tree[curlen].fc.freq + count);
+ } else if (curlen != 0) {
+ if (curlen != prevlen) state.ts.bl_tree[curlen].fc.freq++;
+ state.ts.bl_tree[REP_3_6].fc.freq++;
+ } else if (count <= 10) {
+ state.ts.bl_tree[REPZ_3_10].fc.freq++;
+ } else {
+ state.ts.bl_tree[REPZ_11_138].fc.freq++;
+ }
+ count = 0; prevlen = curlen;
+ if (nextlen == 0) {
+ max_count = 138, min_count = 3;
+ } else if (curlen == nextlen) {
+ max_count = 6, min_count = 3;
+ } else {
+ max_count = 7, min_count = 4;
+ }
+ }
+}
+
+/* ===========================================================================
+ * Send a literal or distance tree in compressed form, using the codes in
+ * bl_tree.
+ */
+void send_tree (TState &state, ct_data *tree, int max_code)
+{
+ int n; /* iterates over all tree elements */
+ int prevlen = -1; /* last emitted length */
+ int curlen; /* length of current code */
+ int nextlen = tree[0].dl.len; /* length of next code */
+ int count = 0; /* repeat count of the current code */
+ int max_count = 7; /* max repeat count */
+ int min_count = 4; /* min repeat count */
+
+ /* tree[max_code+1].dl.len = -1; */ /* guard already set */
+ if (nextlen == 0) max_count = 138, min_count = 3;
+
+ for (n = 0; n <= max_code; n++) {
+ curlen = nextlen; nextlen = tree[n+1].dl.len;
+ if (++count < max_count && curlen == nextlen) {
+ continue;
+ } else if (count < min_count) {
+ do { send_code(state, curlen, state.ts.bl_tree); } while (--count != 0);
+
+ } else if (curlen != 0) {
+ if (curlen != prevlen) {
+ send_code(state, curlen, state.ts.bl_tree); count--;
+ }
+ Assert(state,count >= 3 && count <= 6, " 3_6?");
+ send_code(state,REP_3_6, state.ts.bl_tree); send_bits(state,count-3, 2);
+
+ } else if (count <= 10) {
+ send_code(state,REPZ_3_10, state.ts.bl_tree); send_bits(state,count-3, 3);
+
+ } else {
+ send_code(state,REPZ_11_138, state.ts.bl_tree); send_bits(state,count-11, 7);
+ }
+ count = 0; prevlen = curlen;
+ if (nextlen == 0) {
+ max_count = 138, min_count = 3;
+ } else if (curlen == nextlen) {
+ max_count = 6, min_count = 3;
+ } else {
+ max_count = 7, min_count = 4;
+ }
+ }
+}
+
+/* ===========================================================================
+ * Construct the Huffman tree for the bit lengths and return the index in
+ * bl_order of the last bit length code to send.
+ */
+int build_bl_tree(TState &state)
+{
+ int max_blindex; /* index of last bit length code of non zero freq */
+
+ /* Determine the bit length frequencies for literal and distance trees */
+ scan_tree(state,(ct_data *)state.ts.dyn_ltree, state.ts.l_desc.max_code);
+ scan_tree(state,(ct_data *)state.ts.dyn_dtree, state.ts.d_desc.max_code);
+
+ /* Build the bit length tree: */
+ build_tree(state,(tree_desc *)(&state.ts.bl_desc));
+ /* opt_len now includes the length of the tree representations, except
+ * the lengths of the bit lengths codes and the 5+5+4 bits for the counts.
+ */
+
+ /* Determine the number of bit length codes to send. The pkzip format
+ * requires that at least 4 bit length codes be sent. (appnote.txt says
+ * 3 but the actual value used is 4.)
+ */
+ for (max_blindex = BL_CODES-1; max_blindex >= 3; max_blindex--) {
+ if (state.ts.bl_tree[bl_order[max_blindex]].dl.len != 0) break;
+ }
+ /* Update opt_len to include the bit length tree and counts */
+ state.ts.opt_len += 3*(max_blindex+1) + 5+5+4;
+ Trace("\ndyn trees: dyn %ld, stat %ld", state.ts.opt_len, state.ts.static_len);
+
+ return max_blindex;
+}
+
+/* ===========================================================================
+ * Send the header for a block using dynamic Huffman trees: the counts, the
+ * lengths of the bit length codes, the literal tree and the distance tree.
+ * IN assertion: lcodes >= 257, dcodes >= 1, blcodes >= 4.
+ */
+void send_all_trees(TState &state,int lcodes, int dcodes, int blcodes)
+{
+ int rank; /* index in bl_order */
+
+ Assert(state,lcodes >= 257 && dcodes >= 1 && blcodes >= 4, "not enough codes");
+ Assert(state,lcodes <= L_CODES && dcodes <= D_CODES && blcodes <= BL_CODES,
+ "too many codes");
+ Trace("\nbl counts: ");
+ send_bits(state,lcodes-257, 5);
+ /* not +255 as stated in appnote.txt 1.93a or -256 in 2.04c */
+ send_bits(state,dcodes-1, 5);
+ send_bits(state,blcodes-4, 4); /* not -3 as stated in appnote.txt */
+ for (rank = 0; rank < blcodes; rank++) {
+ Trace("\nbl code %2d ", bl_order[rank]);
+ send_bits(state,state.ts.bl_tree[bl_order[rank]].dl.len, 3);
+ }
+ Trace("\nbl tree: sent %ld", state.bs.bits_sent);
+
+ send_tree(state,(ct_data *)state.ts.dyn_ltree, lcodes-1); /* send the literal tree */
+ Trace("\nlit tree: sent %ld", state.bs.bits_sent);
+
+ send_tree(state,(ct_data *)state.ts.dyn_dtree, dcodes-1); /* send the distance tree */
+ Trace("\ndist tree: sent %ld", state.bs.bits_sent);
+}
+
+/* ===========================================================================
+ * Determine the best encoding for the current block: dynamic trees, static
+ * trees or store, and output the encoded block to the zip file. This function
+ * returns the total compressed length (in bytes) for the file so far.
+ */
+ulg flush_block(TState &state,char *buf, ulg stored_len, int eof)
+{
+ ulg opt_lenb, static_lenb; /* opt_len and static_len in bytes */
+ int max_blindex; /* index of last bit length code of non zero freq */
+
+ state.ts.flag_buf[state.ts.last_flags] = state.ts.flags; /* Save the flags for the last 8 items */
+
+ /* Check if the file is ascii or binary */
+ if (*state.ts.file_type == (ush)UNKNOWN) set_file_type(state);
+
+ /* Construct the literal and distance trees */
+ build_tree(state,(tree_desc *)(&state.ts.l_desc));
+ Trace("\nlit data: dyn %ld, stat %ld", state.ts.opt_len, state.ts.static_len);
+
+ build_tree(state,(tree_desc *)(&state.ts.d_desc));
+ Trace("\ndist data: dyn %ld, stat %ld", state.ts.opt_len, state.ts.static_len);
+ /* At this point, opt_len and static_len are the total bit lengths of
+ * the compressed block data, excluding the tree representations.
+ */
+
+ /* Build the bit length tree for the above two trees, and get the index
+ * in bl_order of the last bit length code to send.
+ */
+ max_blindex = build_bl_tree(state);
+
+ /* Determine the best encoding. Compute first the block length in bytes */
+ opt_lenb = (state.ts.opt_len+3+7)>>3;
+ static_lenb = (state.ts.static_len+3+7)>>3;
+ state.ts.input_len += stored_len; /* for debugging only */
+
+ Trace("\nopt %lu(%lu) stat %lu(%lu) stored %lu lit %u dist %u ",
+ opt_lenb, state.ts.opt_len, static_lenb, state.ts.static_len, stored_len,
+ state.ts.last_lit, state.ts.last_dist);
+
+ if (static_lenb <= opt_lenb) opt_lenb = static_lenb;
+
+ // Originally, zip allowed the file to be transformed from a compressed
+ // into a stored file in the case where compression failed, there
+ // was only one block, and it was allowed to change. I've removed this
+ // possibility since the code's cleaner if no changes are allowed.
+ //if (stored_len <= opt_lenb && eof && state.ts.cmpr_bytelen == 0L
+ // && state.ts.cmpr_len_bits == 0L && state.seekable)
+ //{ // && state.ts.file_method != NULL
+ // // Since LIT_BUFSIZE <= 2*WSIZE, the input data must be there:
+ // Assert(state,buf!=NULL,"block vanished");
+ // copy_block(state,buf, (unsigned)stored_len, 0); // without header
+ // state.ts.cmpr_bytelen = stored_len;
+ // Assert(state,false,"unimplemented *state.ts.file_method = STORE;");
+ // //*state.ts.file_method = STORE;
+ //}
+ //else
+ if (stored_len+4 <= opt_lenb && buf != (char*)NULL) {
+ /* 4: two words for the lengths */
+ /* The test buf != NULL is only necessary if LIT_BUFSIZE > WSIZE.
+ * Otherwise we can't have processed more than WSIZE input bytes since
+ * the last block flush, because compression would have been
+ * successful. If LIT_BUFSIZE <= WSIZE, it is never too late to
+ * transform a block into a stored block.
+ */
+ send_bits(state,(STORED_BLOCK<<1)+eof, 3); /* send block type */
+ state.ts.cmpr_bytelen += ((state.ts.cmpr_len_bits + 3 + 7) >> 3) + stored_len + 4;
+ state.ts.cmpr_len_bits = 0L;
+
+ copy_block(state,buf, (unsigned)stored_len, 1); /* with header */
+ }
+ else if (static_lenb == opt_lenb) {
+ send_bits(state,(STATIC_TREES<<1)+eof, 3);
+ compress_block(state,(ct_data *)state.ts.static_ltree, (ct_data *)state.ts.static_dtree);
+ state.ts.cmpr_len_bits += 3 + state.ts.static_len;
+ state.ts.cmpr_bytelen += state.ts.cmpr_len_bits >> 3;
+ state.ts.cmpr_len_bits &= 7L;
+ }
+ else {
+ send_bits(state,(DYN_TREES<<1)+eof, 3);
+ send_all_trees(state,state.ts.l_desc.max_code+1, state.ts.d_desc.max_code+1, max_blindex+1);
+ compress_block(state,(ct_data *)state.ts.dyn_ltree, (ct_data *)state.ts.dyn_dtree);
+ state.ts.cmpr_len_bits += 3 + state.ts.opt_len;
+ state.ts.cmpr_bytelen += state.ts.cmpr_len_bits >> 3;
+ state.ts.cmpr_len_bits &= 7L;
+ }
+ Assert(state,((state.ts.cmpr_bytelen << 3) + state.ts.cmpr_len_bits) == state.bs.bits_sent, "bad compressed size");
+ init_block(state);
+
+ if (eof) {
+ // Assert(state,input_len == isize, "bad input size");
+ bi_windup(state);
+ state.ts.cmpr_len_bits += 7; /* align on byte boundary */
+ }
+ Trace("\n");
+
+ return state.ts.cmpr_bytelen + (state.ts.cmpr_len_bits >> 3);
+}
+
+/* ===========================================================================
+ * Save the match info and tally the frequency counts. Return true if
+ * the current block must be flushed.
+ */
+int ct_tally (TState &state,int dist, int lc)
+{
+ state.ts.l_buf[state.ts.last_lit++] = (uch)lc;
+ if (dist == 0) {
+ /* lc is the unmatched char */
+ state.ts.dyn_ltree[lc].fc.freq++;
+ } else {
+ /* Here, lc is the match length - MIN_MATCH */
+ dist--; /* dist = match distance - 1 */
+ Assert(state,(ush)dist < (ush)MAX_DIST &&
+ (ush)lc <= (ush)(MAX_MATCH-MIN_MATCH) &&
+ (ush)d_code(dist) < (ush)D_CODES, "ct_tally: bad match");
+
+ state.ts.dyn_ltree[state.ts.length_code[lc]+LITERALS+1].fc.freq++;
+ state.ts.dyn_dtree[d_code(dist)].fc.freq++;
+
+ state.ts.d_buf[state.ts.last_dist++] = (ush)dist;
+ state.ts.flags |= state.ts.flag_bit;
+ }
+ state.ts.flag_bit <<= 1;
+
+ /* Output the flags if they fill a byte: */
+ if ((state.ts.last_lit & 7) == 0) {
+ state.ts.flag_buf[state.ts.last_flags++] = state.ts.flags;
+ state.ts.flags = 0, state.ts.flag_bit = 1;
+ }
+ /* Try to guess if it is profitable to stop the current block here */
+ if (state.level > 2 && (state.ts.last_lit & 0xfff) == 0) {
+ /* Compute an upper bound for the compressed length */
+ ulg out_length = (ulg)state.ts.last_lit*8L;
+ ulg in_length = (ulg)state.ds.strstart-state.ds.block_start;
+ int dcode;
+ for (dcode = 0; dcode < D_CODES; dcode++) {
+ out_length += (ulg)state.ts.dyn_dtree[dcode].fc.freq*(5L+extra_dbits[dcode]);
+ }
+ out_length >>= 3;
+ Trace("\nlast_lit %u, last_dist %u, in %ld, out ~%ld(%ld%%) ",
+ state.ts.last_lit, state.ts.last_dist, in_length, out_length,
+ 100L - out_length*100L/in_length);
+ if (state.ts.last_dist < state.ts.last_lit/2 && out_length < in_length/2) return 1;
+ }
+ return (state.ts.last_lit == LIT_BUFSIZE-1 || state.ts.last_dist == DIST_BUFSIZE);
+ /* We avoid equality with LIT_BUFSIZE because of wraparound at 64K
+ * on 16 bit machines and because stored blocks are restricted to
+ * 64K-1 bytes.
+ */
+}
+
+/* ===========================================================================
+ * Send the block data compressed using the given Huffman trees
+ */
+void compress_block(TState &state,ct_data *ltree, ct_data *dtree)
+{
+ unsigned dist; /* distance of matched string */
+ int lc; /* match length or unmatched char (if dist == 0) */
+ unsigned lx = 0; /* running index in l_buf */
+ unsigned dx = 0; /* running index in d_buf */
+ unsigned fx = 0; /* running index in flag_buf */
+ uch flag = 0; /* current flags */
+ unsigned code; /* the code to send */
+ int extra; /* number of extra bits to send */
+
+ if (state.ts.last_lit != 0) do {
+ if ((lx & 7) == 0) flag = state.ts.flag_buf[fx++];
+ lc = state.ts.l_buf[lx++];
+ if ((flag & 1) == 0) {
+ send_code(state,lc, ltree); /* send a literal byte */
+ } else {
+ /* Here, lc is the match length - MIN_MATCH */
+ code = state.ts.length_code[lc];
+ send_code(state,code+LITERALS+1, ltree); /* send the length code */
+ extra = extra_lbits[code];
+ if (extra != 0) {
+ lc -= state.ts.base_length[code];
+ send_bits(state,lc, extra); /* send the extra length bits */
+ }
+ dist = state.ts.d_buf[dx++];
+ /* Here, dist is the match distance - 1 */
+ code = d_code(dist);
+ Assert(state,code < D_CODES, "bad d_code");
+
+ send_code(state,code, dtree); /* send the distance code */
+ extra = extra_dbits[code];
+ if (extra != 0) {
+ dist -= state.ts.base_dist[code];
+ send_bits(state,dist, extra); /* send the extra distance bits */
+ }
+ } /* literal or match pair ? */
+ flag >>= 1;
+ } while (lx < state.ts.last_lit);
+
+ send_code(state,END_BLOCK, ltree);
+}
+
+/* ===========================================================================
+ * Set the file type to ASCII or BINARY, using a crude approximation:
+ * binary if more than 20% of the bytes are <= 6 or >= 128, ascii otherwise.
+ * IN assertion: the fields freq of dyn_ltree are set and the total of all
+ * frequencies does not exceed 64K (to fit in an int on 16 bit machines).
+ */
+void set_file_type(TState &state)
+{
+ int n = 0;
+ unsigned ascii_freq = 0;
+ unsigned bin_freq = 0;
+ while (n < 7) bin_freq += state.ts.dyn_ltree[n++].fc.freq;
+ while (n < 128) ascii_freq += state.ts.dyn_ltree[n++].fc.freq;
+ while (n < LITERALS) bin_freq += state.ts.dyn_ltree[n++].fc.freq;
+ *state.ts.file_type = (ush)(bin_freq > (ascii_freq >> 2) ? BINARY : ASCII);
+}
+
+
+/* ===========================================================================
+ * Initialize the bit string routines.
+ */
+void bi_init (TState &state,char *tgt_buf, unsigned tgt_size, int flsh_allowed)
+{
+ state.bs.out_buf = tgt_buf;
+ state.bs.out_size = tgt_size;
+ state.bs.out_offset = 0;
+ state.bs.flush_flg = flsh_allowed;
+
+ state.bs.bi_buf = 0;
+ state.bs.bi_valid = 0;
+ state.bs.bits_sent = 0L;
+}
+
+/* ===========================================================================
+ * Send a value on a given number of bits.
+ * IN assertion: length <= 16 and value fits in length bits.
+ */
+void send_bits(TState &state,int value, int length)
+{
+ Assert(state,length > 0 && length <= 15, "invalid length");
+ state.bs.bits_sent += (ulg)length;
+ /* If not enough room in bi_buf, use (bi_valid) bits from bi_buf and
+ * (Buf_size - bi_valid) bits from value to flush the filled bi_buf,
+ * then fill in the rest of (value), leaving (length - (Buf_size-bi_valid))
+ * unused bits in bi_buf.
+ */
+ state.bs.bi_buf |= (value << state.bs.bi_valid);
+ state.bs.bi_valid += length;
+ if (state.bs.bi_valid > (int)Buf_size) {
+ PUTSHORT(state,state.bs.bi_buf);
+ state.bs.bi_valid -= Buf_size;
+ state.bs.bi_buf = (unsigned)value >> (length - state.bs.bi_valid);
+ }
+}
+
+/* ===========================================================================
+ * Reverse the first len bits of a code, using straightforward code (a faster
+ * method would use a table)
+ * IN assertion: 1 <= len <= 15
+ */
+unsigned bi_reverse(unsigned code, int len)
+{
+ register unsigned res = 0;
+ do {
+ res |= code & 1;
+ code >>= 1, res <<= 1;
+ } while (--len > 0);
+ return res >> 1;
+}
+
+/* ===========================================================================
+ * Write out any remaining bits in an incomplete byte.
+ */
+void bi_windup(TState &state)
+{
+ if (state.bs.bi_valid > 8) {
+ PUTSHORT(state,state.bs.bi_buf);
+ } else if (state.bs.bi_valid > 0) {
+ PUTBYTE(state,state.bs.bi_buf);
+ }
+ if (state.bs.flush_flg) {
+ state.flush_outbuf(state.param,state.bs.out_buf, &state.bs.out_offset);
+ }
+ state.bs.bi_buf = 0;
+ state.bs.bi_valid = 0;
+ state.bs.bits_sent = (state.bs.bits_sent+7) & ~7;
+}
+
+/* ===========================================================================
+ * Copy a stored block to the zip file, storing first the length and its
+ * one's complement if requested.
+ */
+void copy_block(TState &state, char *block, unsigned len, int header)
+{
+ bi_windup(state); /* align on byte boundary */
+
+ if (header) {
+ PUTSHORT(state,(ush)len);
+ PUTSHORT(state,(ush)~len);
+ state.bs.bits_sent += 2*16;
+ }
+ if (state.bs.flush_flg) {
+ state.flush_outbuf(state.param,state.bs.out_buf, &state.bs.out_offset);
+ state.bs.out_offset = len;
+ state.flush_outbuf(state.param,block, &state.bs.out_offset);
+ } else if (state.bs.out_offset + len > state.bs.out_size) {
+ Assert(state,false,"output buffer too small for in-memory compression");
+ } else {
+ memcpy(state.bs.out_buf + state.bs.out_offset, block, len);
+ state.bs.out_offset += len;
+ }
+ state.bs.bits_sent += (ulg)len<<3;
+}
+
+
+
+
+
+
+
+
+/* ===========================================================================
+ * Prototypes for functions.
+ */
+
+void fill_window (TState &state);
+ulg deflate_fast (TState &state);
+
+int longest_match (TState &state,IPos cur_match);
+
+
+/* ===========================================================================
+ * Update a hash value with the given input byte
+ * IN assertion: all calls to to UPDATE_HASH are made with consecutive
+ * input characters, so that a running hash key can be computed from the
+ * previous key instead of complete recalculation each time.
+ */
+#define UPDATE_HASH(h,c) (h = (((h)<<H_SHIFT) ^ (c)) & HASH_MASK)
+
+/* ===========================================================================
+ * Insert string s in the dictionary and set match_head to the previous _head
+ * of the hash chain (the most recent string with same hash key). Return
+ * the previous length of the hash chain.
+ * IN assertion: all calls to to INSERT_STRING are made with consecutive
+ * input characters and the first MIN_MATCH bytes of s are valid
+ * (except for the last MIN_MATCH-1 bytes of the input file).
+ */
+#define INSERT_STRING(s, match_head) \
+ (UPDATE_HASH(state.ds.ins_h, state.ds.window[(s) + (MIN_MATCH-1)]), \
+ state.ds.prev[(s) & WMASK] = match_head = state.ds.head[state.ds.ins_h], \
+ state.ds.head[state.ds.ins_h] = (s))
+
+/* ===========================================================================
+ * Initialize the "longest match" routines for a new file
+ *
+ * IN assertion: window_size is > 0 if the input file is already read or
+ * mmap'ed in the window[] array, 0 otherwise. In the first case,
+ * window_size is sufficient to contain the whole input file plus
+ * MIN_LOOKAHEAD bytes (to avoid referencing memory beyond the end
+ * of window[] when looking for matches towards the end).
+ */
+void lm_init (TState &state, int pack_level, ush *flags)
+{
+ register unsigned j;
+
+ Assert(state,pack_level>=1 && pack_level<=8,"bad pack level");
+
+ /* Do not slide the window if the whole input is already in memory
+ * (window_size > 0)
+ */
+ state.ds.sliding = 0;
+ if (state.ds.window_size == 0L) {
+ state.ds.sliding = 1;
+ state.ds.window_size = (ulg)2L*WSIZE;
+ }
+
+ /* Initialize the hash table (avoiding 64K overflow for 16 bit systems).
+ * prev[] will be initialized on the fly.
+ */
+ state.ds.head[HASH_SIZE-1] = NIL;
+ memset((char*)state.ds.head, NIL, (unsigned)(HASH_SIZE-1)*sizeof(*state.ds.head));
+
+ /* Set the default configuration parameters:
+ */
+ state.ds.max_lazy_match = configuration_table[pack_level].max_lazy;
+ state.ds.good_match = configuration_table[pack_level].good_length;
+ state.ds.nice_match = configuration_table[pack_level].nice_length;
+ state.ds.max_chain_length = configuration_table[pack_level].max_chain;
+ if (pack_level <= 2) {
+ *flags |= FAST;
+ } else if (pack_level >= 8) {
+ *flags |= SLOW;
+ }
+ /* ??? reduce max_chain_length for binary files */
+
+ state.ds.strstart = 0;
+ state.ds.block_start = 0L;
+
+ j = WSIZE;
+ j <<= 1; // Can read 64K in one step
+ state.ds.lookahead = state.readfunc(state, (char*)state.ds.window, j);
+
+ if (state.ds.lookahead == 0 || state.ds.lookahead == (unsigned)EOF) {
+ state.ds.eofile = 1, state.ds.lookahead = 0;
+ return;
+ }
+ state.ds.eofile = 0;
+ /* Make sure that we always have enough lookahead. This is important
+ * if input comes from a device such as a tty.
+ */
+ if (state.ds.lookahead < MIN_LOOKAHEAD) fill_window(state);
+
+ state.ds.ins_h = 0;
+ for (j=0; j<MIN_MATCH-1; j++) UPDATE_HASH(state.ds.ins_h, state.ds.window[j]);
+ /* If lookahead < MIN_MATCH, ins_h is garbage, but this is
+ * not important since only literal bytes will be emitted.
+ */
+}
+
+
+/* ===========================================================================
+ * Set match_start to the longest match starting at the given string and
+ * return its length. Matches shorter or equal to prev_length are discarded,
+ * in which case the result is equal to prev_length and match_start is
+ * garbage.
+ * IN assertions: cur_match is the _head of the hash chain for the current
+ * string (strstart) and its distance is <= MAX_DIST, and prev_length >= 1
+ */
+// For 80x86 and 680x0 and ARM, an optimized version is in match.asm or
+// match.S. The code is functionally equivalent, so you can use the C version
+// if desired. Which I do so desire!
+int longest_match(TState &state,IPos cur_match)
+{
+ unsigned chain_length = state.ds.max_chain_length; /* max hash chain length */
+ register uch far *scan = state.ds.window + state.ds.strstart; /* current string */
+ register uch far *match; /* matched string */
+ register int len; /* length of current match */
+ int best_len = state.ds.prev_length; /* best match length so far */
+ IPos limit = state.ds.strstart > (IPos)MAX_DIST ? state.ds.strstart - (IPos)MAX_DIST : NIL;
+ /* Stop when cur_match becomes <= limit. To simplify the code,
+ * we prevent matches with the string of window index 0.
+ */
+
+ // The code is optimized for HASH_BITS >= 8 and MAX_MATCH-2 multiple of 16.
+ // It is easy to get rid of this optimization if necessary.
+ Assert(state,HASH_BITS>=8 && MAX_MATCH==258,"Code too clever");
+
+
+
+ register uch far *strend = state.ds.window + state.ds.strstart + MAX_MATCH;
+ register uch scan_end1 = scan[best_len-1];
+ register uch scan_end = scan[best_len];
+
+ /* Do not waste too much time if we already have a good match: */
+ if (state.ds.prev_length >= state.ds.good_match) {
+ chain_length >>= 2;
+ }
+
+ Assert(state,state.ds.strstart <= state.ds.window_size-MIN_LOOKAHEAD, "insufficient lookahead");
+
+ do {
+ Assert(state,cur_match < state.ds.strstart, "no future");
+ match = state.ds.window + cur_match;
+
+ /* Skip to next match if the match length cannot increase
+ * or if the match length is less than 2:
+ */
+ if (match[best_len] != scan_end ||
+ match[best_len-1] != scan_end1 ||
+ *match != *scan ||
+ *++match != scan[1]) continue;
+
+ /* The check at best_len-1 can be removed because it will be made
+ * again later. (This heuristic is not always a win.)
+ * It is not necessary to compare scan[2] and match[2] since they
+ * are always equal when the other bytes match, given that
+ * the hash keys are equal and that HASH_BITS >= 8.
+ */
+ scan += 2, match++;
+
+ /* We check for insufficient lookahead only every 8th comparison;
+ * the 256th check will be made at strstart+258.
+ */
+ do {
+ } while (*++scan == *++match && *++scan == *++match &&
+ *++scan == *++match && *++scan == *++match &&
+ *++scan == *++match && *++scan == *++match &&
+ *++scan == *++match && *++scan == *++match &&
+ scan < strend);
+
+ Assert(state,scan <= state.ds.window+(unsigned)(state.ds.window_size-1), "wild scan");
+
+ len = MAX_MATCH - (int)(strend - scan);
+ scan = strend - MAX_MATCH;
+
+
+ if (len > best_len) {
+ state.ds.match_start = cur_match;
+ best_len = len;
+ if (len >= state.ds.nice_match) break;
+ scan_end1 = scan[best_len-1];
+ scan_end = scan[best_len];
+ }
+ } while ((cur_match = state.ds.prev[cur_match & WMASK]) > limit
+ && --chain_length != 0);
+
+ return best_len;
+}
+
+
+
+#define check_match(state,start, match, length)
+// or alternatively...
+//void check_match(TState &state,IPos start, IPos match, int length)
+//{ // check that the match is indeed a match
+// if (memcmp((char*)state.ds.window + match,
+// (char*)state.ds.window + start, length) != EQUAL) {
+// fprintf(stderr,
+// " start %d, match %d, length %d\n",
+// start, match, length);
+// error("invalid match");
+// }
+// if (state.verbose > 1) {
+// fprintf(stderr,"\\[%d,%d]", start-match, length);
+// do { fprintf(stdout,"%c",state.ds.window[start++]); } while (--length != 0);
+// }
+//}
+
+/* ===========================================================================
+ * Fill the window when the lookahead becomes insufficient.
+ * Updates strstart and lookahead, and sets eofile if end of input file.
+ *
+ * IN assertion: lookahead < MIN_LOOKAHEAD && strstart + lookahead > 0
+ * OUT assertions: strstart <= window_size-MIN_LOOKAHEAD
+ * At least one byte has been read, or eofile is set; file reads are
+ * performed for at least two bytes (required for the translate_eol option).
+ */
+void fill_window(TState &state)
+{
+ register unsigned n, m;
+ unsigned more; /* Amount of free space at the end of the window. */
+
+ do {
+ more = (unsigned)(state.ds.window_size - (ulg)state.ds.lookahead - (ulg)state.ds.strstart);
+
+ /* If the window is almost full and there is insufficient lookahead,
+ * move the upper half to the lower one to make room in the upper half.
+ */
+ if (more == (unsigned)EOF) {
+ /* Very unlikely, but possible on 16 bit machine if strstart == 0
+ * and lookahead == 1 (input done one byte at time)
+ */
+ more--;
+
+ /* For MMAP or BIG_MEM, the whole input file is already in memory so
+ * we must not perform sliding. We must however call (*read_buf)() in
+ * order to compute the crc, update lookahead and possibly set eofile.
+ */
+ } else if (state.ds.strstart >= WSIZE+MAX_DIST && state.ds.sliding) {
+
+ /* By the IN assertion, the window is not empty so we can't confuse
+ * more == 0 with more == 64K on a 16 bit machine.
+ */
+ memcpy((char*)state.ds.window, (char*)state.ds.window+WSIZE, (unsigned)WSIZE);
+ state.ds.match_start -= WSIZE;
+ state.ds.strstart -= WSIZE; /* we now have strstart >= MAX_DIST: */
+
+ state.ds.block_start -= (long) WSIZE;
+
+ for (n = 0; n < HASH_SIZE; n++) {
+ m = state.ds.head[n];
+ state.ds.head[n] = (Pos)(m >= WSIZE ? m-WSIZE : NIL);
+ }
+ for (n = 0; n < WSIZE; n++) {
+ m = state.ds.prev[n];
+ state.ds.prev[n] = (Pos)(m >= WSIZE ? m-WSIZE : NIL);
+ /* If n is not on any hash chain, prev[n] is garbage but
+ * its value will never be used.
+ */
+ }
+ more += WSIZE;
+ }
+ if (state.ds.eofile) return;
+
+ /* If there was no sliding:
+ * strstart <= WSIZE+MAX_DIST-1 && lookahead <= MIN_LOOKAHEAD - 1 &&
+ * more == window_size - lookahead - strstart
+ * => more >= window_size - (MIN_LOOKAHEAD-1 + WSIZE + MAX_DIST-1)
+ * => more >= window_size - 2*WSIZE + 2
+ * In the MMAP or BIG_MEM case (not yet supported in gzip),
+ * window_size == input_size + MIN_LOOKAHEAD &&
+ * strstart + lookahead <= input_size => more >= MIN_LOOKAHEAD.
+ * Otherwise, window_size == 2*WSIZE so more >= 2.
+ * If there was sliding, more >= WSIZE. So in all cases, more >= 2.
+ */
+ Assert(state,more >= 2, "more < 2");
+
+ n = state.readfunc(state, (char*)state.ds.window+state.ds.strstart+state.ds.lookahead, more);
+
+ if (n == 0 || n == (unsigned)EOF) {
+ state.ds.eofile = 1;
+ } else {
+ state.ds.lookahead += n;
+ }
+ } while (state.ds.lookahead < MIN_LOOKAHEAD && !state.ds.eofile);
+}
+
+/* ===========================================================================
+ * Flush the current block, with given end-of-file flag.
+ * IN assertion: strstart is set to the end of the current match.
+ */
+#define FLUSH_BLOCK(state,eof) \
+ flush_block(state,state.ds.block_start >= 0L ? (char*)&state.ds.window[(unsigned)state.ds.block_start] : \
+ (char*)NULL, (long)state.ds.strstart - state.ds.block_start, (eof))
+
+/* ===========================================================================
+ * Processes a new input file and return its compressed length. This
+ * function does not perform lazy evaluation of matches and inserts
+ * new strings in the dictionary only for unmatched strings or for short
+ * matches. It is used only for the fast compression options.
+ */
+ulg deflate_fast(TState &state)
+{
+ IPos hash_head = NIL; /* _head of the hash chain */
+ int flush; /* set if current block must be flushed */
+ unsigned match_length = 0; /* length of best match */
+
+ state.ds.prev_length = MIN_MATCH-1;
+ while (state.ds.lookahead != 0) {
+ /* Insert the string window[strstart .. strstart+2] in the
+ * dictionary, and set hash_head to the _head of the hash chain:
+ */
+ if (state.ds.lookahead >= MIN_MATCH)
+ INSERT_STRING(state.ds.strstart, hash_head);
+
+ /* Find the longest match, discarding those <= prev_length.
+ * At this point we have always match_length < MIN_MATCH
+ */
+ if (hash_head != NIL && state.ds.strstart - hash_head <= MAX_DIST) {
+ /* To simplify the code, we prevent matches with the string
+ * of window index 0 (in particular we have to avoid a match
+ * of the string with itself at the start of the input file).
+ */
+ /* Do not look for matches beyond the end of the input.
+ * This is necessary to make deflate deterministic.
+ */
+ if ((unsigned)state.ds.nice_match > state.ds.lookahead) state.ds.nice_match = (int)state.ds.lookahead;
+ match_length = longest_match (state,hash_head);
+ /* longest_match() sets match_start */
+ if (match_length > state.ds.lookahead) match_length = state.ds.lookahead;
+ }
+ if (match_length >= MIN_MATCH) {
+ check_match(state,state.ds.strstart, state.ds.match_start, match_length);
+
+ flush = ct_tally(state,state.ds.strstart-state.ds.match_start, match_length - MIN_MATCH);
+
+ state.ds.lookahead -= match_length;
+
+ /* Insert new strings in the hash table only if the match length
+ * is not too large. This saves time but degrades compression.
+ */
+ if (match_length <= state.ds.max_insert_length
+ && state.ds.lookahead >= MIN_MATCH) {
+ match_length--; /* string at strstart already in hash table */
+ do {
+ state.ds.strstart++;
+ INSERT_STRING(state.ds.strstart, hash_head);
+ /* strstart never exceeds WSIZE-MAX_MATCH, so there are
+ * always MIN_MATCH bytes ahead.
+ */
+ } while (--match_length != 0);
+ state.ds.strstart++;
+ } else {
+ state.ds.strstart += match_length;
+ match_length = 0;
+ state.ds.ins_h = state.ds.window[state.ds.strstart];
+ UPDATE_HASH(state.ds.ins_h, state.ds.window[state.ds.strstart+1]);
+ Assert(state,MIN_MATCH==3,"Call UPDATE_HASH() MIN_MATCH-3 more times");
+ }
+ } else {
+ /* No match, output a literal byte */
+ flush = ct_tally (state,0, state.ds.window[state.ds.strstart]);
+ state.ds.lookahead--;
+ state.ds.strstart++;
+ }
+ if (flush) FLUSH_BLOCK(state,0), state.ds.block_start = state.ds.strstart;
+
+ /* Make sure that we always have enough lookahead, except
+ * at the end of the input file. We need MAX_MATCH bytes
+ * for the next match, plus MIN_MATCH bytes to insert the
+ * string following the next match.
+ */
+ if (state.ds.lookahead < MIN_LOOKAHEAD) fill_window(state);
+ }
+ return FLUSH_BLOCK(state,1); /* eof */
+}
+
+/* ===========================================================================
+ * Same as above, but achieves better compression. We use a lazy
+ * evaluation for matches: a match is finally adopted only if there is
+ * no better match at the next window position.
+ */
+ulg deflate(TState &state)
+{
+ IPos hash_head = NIL; /* _head of hash chain */
+ IPos prev_match; /* previous match */
+ int flush; /* set if current block must be flushed */
+ int match_available = 0; /* set if previous match exists */
+ register unsigned match_length = MIN_MATCH-1; /* length of best match */
+
+ if (state.level <= 3) return deflate_fast(state); /* optimized for speed */
+
+ /* Process the input block. */
+ while (state.ds.lookahead != 0) {
+ /* Insert the string window[strstart .. strstart+2] in the
+ * dictionary, and set hash_head to the _head of the hash chain:
+ */
+ if (state.ds.lookahead >= MIN_MATCH)
+ INSERT_STRING(state.ds.strstart, hash_head);
+
+ /* Find the longest match, discarding those <= prev_length.
+ */
+ state.ds.prev_length = match_length, prev_match = state.ds.match_start;
+ match_length = MIN_MATCH-1;
+
+ if (hash_head != NIL && state.ds.prev_length < state.ds.max_lazy_match &&
+ state.ds.strstart - hash_head <= MAX_DIST) {
+ /* To simplify the code, we prevent matches with the string
+ * of window index 0 (in particular we have to avoid a match
+ * of the string with itself at the start of the input file).
+ */
+ /* Do not look for matches beyond the end of the input.
+ * This is necessary to make deflate deterministic.
+ */
+ if ((unsigned)state.ds.nice_match > state.ds.lookahead) state.ds.nice_match = (int)state.ds.lookahead;
+ match_length = longest_match (state,hash_head);
+ /* longest_match() sets match_start */
+ if (match_length > state.ds.lookahead) match_length = state.ds.lookahead;
+
+ /* Ignore a length 3 match if it is too distant: */
+ if (match_length == MIN_MATCH && state.ds.strstart-state.ds.match_start > TOO_FAR){
+ /* If prev_match is also MIN_MATCH, match_start is garbage
+ * but we will ignore the current match anyway.
+ */
+ match_length = MIN_MATCH-1;
+ }
+ }
+ /* If there was a match at the previous step and the current
+ * match is not better, output the previous match:
+ */
+ if (state.ds.prev_length >= MIN_MATCH && match_length <= state.ds.prev_length) {
+ unsigned max_insert = state.ds.strstart + state.ds.lookahead - MIN_MATCH;
+ check_match(state,state.ds.strstart-1, prev_match, state.ds.prev_length);
+ flush = ct_tally(state,state.ds.strstart-1-prev_match, state.ds.prev_length - MIN_MATCH);
+
+ /* Insert in hash table all strings up to the end of the match.
+ * strstart-1 and strstart are already inserted.
+ */
+ state.ds.lookahead -= state.ds.prev_length-1;
+ state.ds.prev_length -= 2;
+ do {
+ if (++state.ds.strstart <= max_insert) {
+ INSERT_STRING(state.ds.strstart, hash_head);
+ /* strstart never exceeds WSIZE-MAX_MATCH, so there are
+ * always MIN_MATCH bytes ahead.
+ */
+ }
+ } while (--state.ds.prev_length != 0);
+ state.ds.strstart++;
+ match_available = 0;
+ match_length = MIN_MATCH-1;
+
+ if (flush) FLUSH_BLOCK(state,0), state.ds.block_start = state.ds.strstart;
+
+ } else if (match_available) {
+ /* If there was no match at the previous position, output a
+ * single literal. If there was a match but the current match
+ * is longer, truncate the previous match to a single literal.
+ */
+ if (ct_tally (state,0, state.ds.window[state.ds.strstart-1])) {
+ FLUSH_BLOCK(state,0), state.ds.block_start = state.ds.strstart;
+ }
+ state.ds.strstart++;
+ state.ds.lookahead--;
+ } else {
+ /* There is no previous match to compare with, wait for
+ * the next step to decide.
+ */
+ match_available = 1;
+ state.ds.strstart++;
+ state.ds.lookahead--;
+ }
+// Assert(state,strstart <= isize && lookahead <= isize, "a bit too far");
+
+ /* Make sure that we always have enough lookahead, except
+ * at the end of the input file. We need MAX_MATCH bytes
+ * for the next match, plus MIN_MATCH bytes to insert the
+ * string following the next match.
+ */
+ if (state.ds.lookahead < MIN_LOOKAHEAD) fill_window(state);
+ }
+ if (match_available) ct_tally (state,0, state.ds.window[state.ds.strstart-1]);
+
+ return FLUSH_BLOCK(state,1); /* eof */
+}
+
+
+
+
+
+
+
+
+
+
+
+
+int putlocal(struct zlist far *z, WRITEFUNC wfunc,void *param)
+{ // Write a local header described by *z to file *f. Return a ZE_ error code.
+ PUTLG(LOCSIG, f);
+ PUTSH(z->ver, f);
+ PUTSH(z->lflg, f);
+ PUTSH(z->how, f);
+ PUTLG(z->tim, f);
+ PUTLG(z->crc, f);
+ PUTLG(z->siz, f);
+ PUTLG(z->len, f);
+ PUTSH(z->nam, f);
+ PUTSH(z->ext, f);
+ size_t res = (size_t)wfunc(param, z->iname, (unsigned int)z->nam);
+ if (res!=z->nam) return ZE_TEMP;
+ if (z->ext)
+ { res = (size_t)wfunc(param, z->extra, (unsigned int)z->ext);
+ if (res!=z->ext) return ZE_TEMP;
+ }
+ return ZE_OK;
+}
+
+int putextended(struct zlist far *z, WRITEFUNC wfunc, void *param)
+{ // Write an extended local header described by *z to file *f. Returns a ZE_ code
+ PUTLG(EXTLOCSIG, f);
+ PUTLG(z->crc, f);
+ PUTLG(z->siz, f);
+ PUTLG(z->len, f);
+ return ZE_OK;
+}
+
+int putcentral(struct zlist far *z, WRITEFUNC wfunc, void *param)
+{ // Write a central header entry of *z to file *f. Returns a ZE_ code.
+ PUTLG(CENSIG, f);
+ PUTSH(z->vem, f);
+ PUTSH(z->ver, f);
+ PUTSH(z->flg, f);
+ PUTSH(z->how, f);
+ PUTLG(z->tim, f);
+ PUTLG(z->crc, f);
+ PUTLG(z->siz, f);
+ PUTLG(z->len, f);
+ PUTSH(z->nam, f);
+ PUTSH(z->cext, f);
+ PUTSH(z->com, f);
+ PUTSH(z->dsk, f);
+ PUTSH(z->att, f);
+ PUTLG(z->atx, f);
+ PUTLG(z->off, f);
+ if ((size_t)wfunc(param, z->iname, (unsigned int)z->nam) != z->nam ||
+ (z->cext && (size_t)wfunc(param, z->cextra, (unsigned int)z->cext) != z->cext) ||
+ (z->com && (size_t)wfunc(param, z->comment, (unsigned int)z->com) != z->com))
+ return ZE_TEMP;
+ return ZE_OK;
+}
+
+
+int putend(int n, ulg s, ulg c, extent m, char *z, WRITEFUNC wfunc, void *param)
+{ // write the end of the central-directory-data to file *f.
+ PUTLG(ENDSIG, f);
+ PUTSH(0, f);
+ PUTSH(0, f);
+ PUTSH(n, f);
+ PUTSH(n, f);
+ PUTLG(s, f);
+ PUTLG(c, f);
+ PUTSH(m, f);
+ // Write the comment, if any
+ if (m && wfunc(param, z, (unsigned int)m) != m) return ZE_TEMP;
+ return ZE_OK;
+}
+
+
+
+
+
+
+const ulg crc_table[256] = {
+ 0x00000000L, 0x77073096L, 0xee0e612cL, 0x990951baL, 0x076dc419L,
+ 0x706af48fL, 0xe963a535L, 0x9e6495a3L, 0x0edb8832L, 0x79dcb8a4L,
+ 0xe0d5e91eL, 0x97d2d988L, 0x09b64c2bL, 0x7eb17cbdL, 0xe7b82d07L,
+ 0x90bf1d91L, 0x1db71064L, 0x6ab020f2L, 0xf3b97148L, 0x84be41deL,
+ 0x1adad47dL, 0x6ddde4ebL, 0xf4d4b551L, 0x83d385c7L, 0x136c9856L,
+ 0x646ba8c0L, 0xfd62f97aL, 0x8a65c9ecL, 0x14015c4fL, 0x63066cd9L,
+ 0xfa0f3d63L, 0x8d080df5L, 0x3b6e20c8L, 0x4c69105eL, 0xd56041e4L,
+ 0xa2677172L, 0x3c03e4d1L, 0x4b04d447L, 0xd20d85fdL, 0xa50ab56bL,
+ 0x35b5a8faL, 0x42b2986cL, 0xdbbbc9d6L, 0xacbcf940L, 0x32d86ce3L,
+ 0x45df5c75L, 0xdcd60dcfL, 0xabd13d59L, 0x26d930acL, 0x51de003aL,
+ 0xc8d75180L, 0xbfd06116L, 0x21b4f4b5L, 0x56b3c423L, 0xcfba9599L,
+ 0xb8bda50fL, 0x2802b89eL, 0x5f058808L, 0xc60cd9b2L, 0xb10be924L,
+ 0x2f6f7c87L, 0x58684c11L, 0xc1611dabL, 0xb6662d3dL, 0x76dc4190L,
+ 0x01db7106L, 0x98d220bcL, 0xefd5102aL, 0x71b18589L, 0x06b6b51fL,
+ 0x9fbfe4a5L, 0xe8b8d433L, 0x7807c9a2L, 0x0f00f934L, 0x9609a88eL,
+ 0xe10e9818L, 0x7f6a0dbbL, 0x086d3d2dL, 0x91646c97L, 0xe6635c01L,
+ 0x6b6b51f4L, 0x1c6c6162L, 0x856530d8L, 0xf262004eL, 0x6c0695edL,
+ 0x1b01a57bL, 0x8208f4c1L, 0xf50fc457L, 0x65b0d9c6L, 0x12b7e950L,
+ 0x8bbeb8eaL, 0xfcb9887cL, 0x62dd1ddfL, 0x15da2d49L, 0x8cd37cf3L,
+ 0xfbd44c65L, 0x4db26158L, 0x3ab551ceL, 0xa3bc0074L, 0xd4bb30e2L,
+ 0x4adfa541L, 0x3dd895d7L, 0xa4d1c46dL, 0xd3d6f4fbL, 0x4369e96aL,
+ 0x346ed9fcL, 0xad678846L, 0xda60b8d0L, 0x44042d73L, 0x33031de5L,
+ 0xaa0a4c5fL, 0xdd0d7cc9L, 0x5005713cL, 0x270241aaL, 0xbe0b1010L,
+ 0xc90c2086L, 0x5768b525L, 0x206f85b3L, 0xb966d409L, 0xce61e49fL,
+ 0x5edef90eL, 0x29d9c998L, 0xb0d09822L, 0xc7d7a8b4L, 0x59b33d17L,
+ 0x2eb40d81L, 0xb7bd5c3bL, 0xc0ba6cadL, 0xedb88320L, 0x9abfb3b6L,
+ 0x03b6e20cL, 0x74b1d29aL, 0xead54739L, 0x9dd277afL, 0x04db2615L,
+ 0x73dc1683L, 0xe3630b12L, 0x94643b84L, 0x0d6d6a3eL, 0x7a6a5aa8L,
+ 0xe40ecf0bL, 0x9309ff9dL, 0x0a00ae27L, 0x7d079eb1L, 0xf00f9344L,
+ 0x8708a3d2L, 0x1e01f268L, 0x6906c2feL, 0xf762575dL, 0x806567cbL,
+ 0x196c3671L, 0x6e6b06e7L, 0xfed41b76L, 0x89d32be0L, 0x10da7a5aL,
+ 0x67dd4accL, 0xf9b9df6fL, 0x8ebeeff9L, 0x17b7be43L, 0x60b08ed5L,
+ 0xd6d6a3e8L, 0xa1d1937eL, 0x38d8c2c4L, 0x4fdff252L, 0xd1bb67f1L,
+ 0xa6bc5767L, 0x3fb506ddL, 0x48b2364bL, 0xd80d2bdaL, 0xaf0a1b4cL,
+ 0x36034af6L, 0x41047a60L, 0xdf60efc3L, 0xa867df55L, 0x316e8eefL,
+ 0x4669be79L, 0xcb61b38cL, 0xbc66831aL, 0x256fd2a0L, 0x5268e236L,
+ 0xcc0c7795L, 0xbb0b4703L, 0x220216b9L, 0x5505262fL, 0xc5ba3bbeL,
+ 0xb2bd0b28L, 0x2bb45a92L, 0x5cb36a04L, 0xc2d7ffa7L, 0xb5d0cf31L,
+ 0x2cd99e8bL, 0x5bdeae1dL, 0x9b64c2b0L, 0xec63f226L, 0x756aa39cL,
+ 0x026d930aL, 0x9c0906a9L, 0xeb0e363fL, 0x72076785L, 0x05005713L,
+ 0x95bf4a82L, 0xe2b87a14L, 0x7bb12baeL, 0x0cb61b38L, 0x92d28e9bL,
+ 0xe5d5be0dL, 0x7cdcefb7L, 0x0bdbdf21L, 0x86d3d2d4L, 0xf1d4e242L,
+ 0x68ddb3f8L, 0x1fda836eL, 0x81be16cdL, 0xf6b9265bL, 0x6fb077e1L,
+ 0x18b74777L, 0x88085ae6L, 0xff0f6a70L, 0x66063bcaL, 0x11010b5cL,
+ 0x8f659effL, 0xf862ae69L, 0x616bffd3L, 0x166ccf45L, 0xa00ae278L,
+ 0xd70dd2eeL, 0x4e048354L, 0x3903b3c2L, 0xa7672661L, 0xd06016f7L,
+ 0x4969474dL, 0x3e6e77dbL, 0xaed16a4aL, 0xd9d65adcL, 0x40df0b66L,
+ 0x37d83bf0L, 0xa9bcae53L, 0xdebb9ec5L, 0x47b2cf7fL, 0x30b5ffe9L,
+ 0xbdbdf21cL, 0xcabac28aL, 0x53b39330L, 0x24b4a3a6L, 0xbad03605L,
+ 0xcdd70693L, 0x54de5729L, 0x23d967bfL, 0xb3667a2eL, 0xc4614ab8L,
+ 0x5d681b02L, 0x2a6f2b94L, 0xb40bbe37L, 0xc30c8ea1L, 0x5a05df1bL,
+ 0x2d02ef8dL
+};
+
+#define CRC32(c, b) (crc_table[((int)(c) ^ (b)) & 0xff] ^ ((c) >> 8))
+#define DO1(buf) crc = CRC32(crc, *buf++)
+#define DO2(buf) DO1(buf); DO1(buf)
+#define DO4(buf) DO2(buf); DO2(buf)
+#define DO8(buf) DO4(buf); DO4(buf)
+
+ulg crc32(ulg crc, const uch *buf, extent len)
+{ if (buf==NULL) return 0L;
+ crc = crc ^ 0xffffffffL;
+ while (len >= 8) {DO8(buf); len -= 8;}
+ if (len) do {DO1(buf);} while (--len);
+ return crc ^ 0xffffffffL; // (instead of ~c for 64-bit machines)
+}
+
+
+
+
+
+
+
+
+bool HasZipSuffix(const char *fn)
+{ const char *ext = fn+strlen(fn);
+ while (ext>fn && *ext!='.') ext--;
+ if (ext==fn && *ext!='.') return false;
+ if (_stricmp(ext,".Z")==0) return true;
+ if (_stricmp(ext,".zip")==0) return true;
+ if (_stricmp(ext,".zoo")==0) return true;
+ if (_stricmp(ext,".arc")==0) return true;
+ if (_stricmp(ext,".lzh")==0) return true;
+ if (_stricmp(ext,".arj")==0) return true;
+ if (_stricmp(ext,".gz")==0) return true;
+ if (_stricmp(ext,".tgz")==0) return true;
+ return false;
+}
+
+
+time_t filetime2timet(const FILETIME ft)
+{ SYSTEMTIME st; FileTimeToSystemTime(&ft,&st);
+ if (st.wYear<1970) {st.wYear=1970; st.wMonth=1; st.wDay=1;}
+ if (st.wYear>=2038) {st.wYear=2037; st.wMonth=12; st.wDay=31;}
+ struct tm tm;
+ tm.tm_sec = st.wSecond;
+ tm.tm_min = st.wMinute;
+ tm.tm_hour = st.wHour;
+ tm.tm_mday = st.wDay;
+ tm.tm_mon = st.wMonth-1;
+ tm.tm_year = st.wYear-1900;
+ tm.tm_isdst = 0;
+ time_t t = mktime(&tm);
+ return t;
+}
+
+
+ZRESULT GetFileInfo(HANDLE hf, ulg *attr, long *size, iztimes *times, ulg *timestamp)
+{
+ DWORD type=GetFileType(hf);
+ if (type!=FILE_TYPE_DISK)
+ return ZR_NOTINITED;
+ // The handle must be a handle to a file
+ // The date and time is returned in a long with the date most significant to allow
+ // unsigned integer comparison of absolute times. The attributes have two
+ // high bytes unix attr, and two low bytes a mapping of that to DOS attr.
+ //struct stat s; int res=stat(fn,&s); if (res!=0) return false;
+ // translate windows file attributes into zip ones.
+ BY_HANDLE_FILE_INFORMATION bhi;
+ BOOL res=GetFileInformationByHandle(hf,&bhi);
+ if (!res)
+ return ZR_NOFILE;
+ DWORD fa=bhi.dwFileAttributes;
+ ulg a=0;
+ // Zip uses the lower word for its interpretation of windows stuff
+ if (fa&FILE_ATTRIBUTE_READONLY) a|=0x01;
+ if (fa&FILE_ATTRIBUTE_HIDDEN) a|=0x02;
+ if (fa&FILE_ATTRIBUTE_SYSTEM) a|=0x04;
+ if (fa&FILE_ATTRIBUTE_DIRECTORY)a|=0x10;
+ if (fa&FILE_ATTRIBUTE_ARCHIVE) a|=0x20;
+ // It uses the upper word for standard unix attr, which we must manually construct
+ if (fa&FILE_ATTRIBUTE_DIRECTORY)a|=0x40000000; // directory
+ else a|=0x80000000; // normal file
+ a|=0x01000000; // readable
+ if (fa&FILE_ATTRIBUTE_READONLY) {}
+ else a|=0x00800000; // writeable
+ // now just a small heuristic to check if it's an executable:
+ DWORD red = 0, hsize=GetFileSize(hf,NULL); if (hsize>40)
+ { SetFilePointer(hf,0,NULL,FILE_BEGIN); unsigned short magic; ReadFile(hf,&magic,sizeof(magic),&red,NULL); red = 0;
+ SetFilePointer(hf,36,NULL,FILE_BEGIN); unsigned long hpos; ReadFile(hf,&hpos,sizeof(hpos),&red,NULL); red = 0;
+ if (magic==0x54AD && hsize>hpos+4+20+28)
+ { SetFilePointer(hf,hpos,NULL,FILE_BEGIN); unsigned long signature; ReadFile(hf,&signature,sizeof(signature),&red,NULL);
+ if (signature==IMAGE_DOS_SIGNATURE || signature==IMAGE_OS2_SIGNATURE
+ || signature==IMAGE_OS2_SIGNATURE_LE || signature==IMAGE_NT_SIGNATURE)
+ { a |= 0x00400000; // executable
+ }
+ }
+ }
+ //
+ if (attr!=NULL) *attr = a;
+ if (size!=NULL) *size = hsize;
+ if (times!=NULL)
+ { // time_t is 32bit number of seconds elapsed since 0:0:0GMT, Jan1, 1970.
+ // but FILETIME is 64bit number of 100-nanosecs since Jan1, 1601
+ times->atime = filetime2timet(bhi.ftLastAccessTime);
+ times->mtime = filetime2timet(bhi.ftLastWriteTime);
+ times->ctime = filetime2timet(bhi.ftCreationTime);
+ }
+ if (timestamp!=NULL)
+ { WORD dosdate,dostime;
+ FileTimeToDosDateTime(&bhi.ftLastWriteTime,&dosdate,&dostime);
+ *timestamp = (WORD)dostime | (((DWORD)dosdate)<<16);
+ }
+ return ZR_OK;
+}
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+
+class TZip
+{ public:
+ TZip() : hfout(0),hmapout(0),zfis(0),obuf(0),hfin(0),writ(0),oerr(false),hasputcen(false),ooffset(0) {}
+ ~TZip() {}
+
+ // These variables say about the file we're writing into
+ // We can write to pipe, file-by-handle, file-by-name, memory-to-memmapfile
+ HANDLE hfout; // if valid, we'll write here (for files or pipes)
+ HANDLE hmapout; // otherwise, we'll write here (for memmap)
+ unsigned ooffset; // for hfout, this is where the pointer was initially
+ ZRESULT oerr; // did a write operation give rise to an error?
+ unsigned writ; // how far have we written. This is maintained by Add, not write(), to avoid confusion over seeks
+ bool ocanseek; // can we seek?
+ char *obuf; // this is where we've locked mmap to view.
+ unsigned int opos; // current pos in the mmap
+ unsigned int mapsize; // the size of the map we created
+ bool hasputcen; // have we yet placed the central directory?
+ //
+ TZipFileInfo *zfis; // each file gets added onto this list, for writing the table at the end
+
+ ZRESULT Create(void *z,unsigned int len,DWORD flags);
+ static unsigned sflush(void *param,const char *buf, unsigned *size);
+ static unsigned swrite(void *param,const char *buf, unsigned size);
+ unsigned int write(const char *buf,unsigned int size);
+ bool oseek(unsigned int pos);
+ ZRESULT GetMemory(void **pbuf, unsigned long *plen);
+ ZRESULT Close();
+
+ // some variables to do with the file currently being read:
+ // I haven't done it object-orientedly here, just put them all
+ // together, since OO didn't seem to make the design any clearer.
+ ulg attr; iztimes times; ulg timestamp; // all open_* methods set these
+ bool iseekable; long isize,ired; // size is not set until close() on pips
+ ulg crc; // crc is not set until close(). iwrit is cumulative
+ HANDLE hfin; bool selfclosehf; // for input files and pipes
+ const char *bufin; unsigned int lenin,posin; // for memory
+ // and a variable for what we've done with the input: (i.e. compressed it!)
+ ulg csize; // compressed size, set by the compression routines
+ // and this is used by some of the compression routines
+ char buf[16384];
+
+
+ ZRESULT open_file(const TCHAR *fn);
+ ZRESULT open_handle(HANDLE hf,unsigned int len);
+ ZRESULT open_mem(void *src,unsigned int len);
+ ZRESULT open_dir();
+ static unsigned sread(TState &s,char *buf,unsigned size);
+ unsigned read(char *buf, unsigned size);
+ ZRESULT iclose();
+
+ ZRESULT ideflate(TZipFileInfo *zfi);
+ ZRESULT istore();
+
+ ZRESULT Add(const char *odstzn, void *src,unsigned int len, DWORD flags);
+ ZRESULT AddCentral();
+
+};
+
+ZRESULT TZip::Create(void *z,unsigned int len,DWORD flags)
+{
+ if (hfout!=0 || hmapout!=0 || obuf!=0 || writ!=0 || oerr!=ZR_OK || hasputcen)
+ return ZR_NOTINITED;
+ //
+ if (flags==ZIP_HANDLE)
+ {
+ HANDLE hf = (HANDLE)z;
+ BOOL res = DuplicateHandle(GetCurrentProcess(),hf,GetCurrentProcess(),&hfout,0,FALSE,DUPLICATE_SAME_ACCESS);
+ if (!res)
+ return ZR_NODUPH;
+ // now we have our own hfout, which we must close. And the caller will close hf
+ DWORD type = GetFileType(hfout);
+ ocanseek = (type==FILE_TYPE_DISK);
+ if (type==FILE_TYPE_DISK)
+ ooffset=SetFilePointer(hfout,0,NULL,FILE_CURRENT);
+ else
+ ooffset=0;
+ return ZR_OK;
+ }
+ else if (flags==ZIP_FILENAME)
+ {
+#ifdef _UNICODE
+ const TCHAR *fn = (const TCHAR*)z;
+ hfout = CreateFileW(fn,GENERIC_WRITE,0,NULL,CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL,NULL);
+#else
+ const char *fn = (const char*)z;
+ hfout = CreateFileA(fn,GENERIC_WRITE,0,NULL,CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL,NULL);
+#endif
+
+ if (hfout==INVALID_HANDLE_VALUE)
+ {
+ hfout=0;
+ return ZR_NOFILE;
+ }
+ ocanseek=true;
+ ooffset=0;
+ return ZR_OK;
+ }
+ else if (flags==ZIP_MEMORY)
+ {
+ unsigned int size = len;
+ if (size==0)
+ return ZR_MEMSIZE;
+ if (z!=0)
+ obuf=(char*)z;
+ else
+ {
+ hmapout = CreateFileMapping(INVALID_HANDLE_VALUE,NULL,PAGE_READWRITE,0,size,NULL);
+ if (hmapout==NULL)
+ return ZR_NOALLOC;
+ obuf = (char*)MapViewOfFile(hmapout,FILE_MAP_ALL_ACCESS,0,0,size);
+ if (obuf==0)
+ {
+ CloseHandle(hmapout);
+ hmapout=0;
+ return ZR_NOALLOC;
+ }
+ }
+ ocanseek=true;
+ opos=0;
+ mapsize=size;
+ return ZR_OK;
+ }
+ else
+ return ZR_ARGS;
+}
+
+
+unsigned TZip::sflush(void *param,const char *buf, unsigned *size)
+{ // static
+ if (*size==0) return 0;
+ TZip *zip = (TZip*)param;
+ unsigned int writ = zip->write(buf,*size);
+ if (writ!=0) *size=0;
+ return writ;
+}
+unsigned TZip::swrite(void *param,const char *buf, unsigned size)
+{ // static
+ if (size==0) return 0;
+ TZip *zip=(TZip*)param; return zip->write(buf,size);
+}
+unsigned int TZip::write(const char *buf,unsigned int size)
+{ if (obuf!=0)
+ { if (opos+size>=mapsize) {oerr=ZR_MEMSIZE; return 0;}
+ memcpy(obuf+opos, buf, size);
+ opos+=size;
+ return size;
+ }
+ else if (hfout!=0)
+ { DWORD writ=0; WriteFile(hfout,buf,size,&writ,NULL);
+ return writ;
+ }
+ oerr=ZR_NOTINITED; return 0;
+}
+
+bool TZip::oseek(unsigned int pos)
+{ if (!ocanseek) {oerr=ZR_SEEK; return false;}
+ if (obuf!=0)
+ { if (pos>=mapsize) {oerr=ZR_MEMSIZE; return false;}
+ opos=pos;
+ return true;
+ }
+ else if (hfout!=0)
+ { SetFilePointer(hfout,pos+ooffset,NULL,FILE_BEGIN);
+ return true;
+ }
+ oerr=ZR_NOTINITED; return 0;
+}
+
+ZRESULT TZip::GetMemory(void **pbuf, unsigned long *plen)
+{ // When the user calls GetMemory, they're presumably at the end
+ // of all their adding. In any case, we have to add the central
+ // directory now, otherwise the memory we tell them won't be complete.
+ if (!hasputcen) AddCentral(); hasputcen=true;
+ if (pbuf!=NULL) *pbuf=(void*)obuf;
+ if (plen!=NULL) *plen=writ;
+ if (obuf==NULL) return ZR_NOTMMAP;
+ return ZR_OK;
+}
+
+ZRESULT TZip::Close()
+{ // if the directory hadn't already been added through a call to GetMemory,
+ // then we do it now
+ ZRESULT res=ZR_OK; if (!hasputcen) res=AddCentral(); hasputcen=true;
+ if (obuf!=0 && hmapout!=0) UnmapViewOfFile(obuf); obuf=0;
+ if (hmapout!=0) CloseHandle(hmapout); hmapout=0;
+ if (hfout!=0) CloseHandle(hfout); hfout=0;
+ return res;
+}
+
+
+
+
+ZRESULT TZip::open_file(const TCHAR *fn)
+{ hfin=0; bufin=0; selfclosehf=false; crc=CRCVAL_INITIAL; isize=0; csize=0; ired=0;
+ if (fn==0) return ZR_ARGS;
+ HANDLE hf = CreateFile(fn,GENERIC_READ,FILE_SHARE_READ,NULL,OPEN_EXISTING,0,NULL);
+ if (hf==INVALID_HANDLE_VALUE) return ZR_NOFILE;
+ ZRESULT res = open_handle(hf,0);
+ if (res!=ZR_OK) {CloseHandle(hf); return res;}
+ selfclosehf=true;
+ return ZR_OK;
+}
+ZRESULT TZip::open_handle(HANDLE hf,unsigned int len)
+{ hfin=0; bufin=0; selfclosehf=false; crc=CRCVAL_INITIAL; isize=0; csize=0; ired=0;
+ if (hf==0 || hf==INVALID_HANDLE_VALUE) return ZR_ARGS;
+ DWORD type = GetFileType(hf);
+ if (type==FILE_TYPE_DISK)
+ { ZRESULT res = GetFileInfo(hf,&attr,&isize,&times,&timestamp);
+ if (res!=ZR_OK) return res;
+ SetFilePointer(hf,0,NULL,FILE_BEGIN); // because GetFileInfo will have screwed it up
+ iseekable=true; hfin=hf;
+ return ZR_OK;
+ }
+ else
+ { attr= 0x80000000; // just a normal file
+ isize = -1; // can't know size until at the end
+ if (len!=0) isize=len; // unless we were told explicitly!
+ iseekable=false;
+ SYSTEMTIME st; GetLocalTime(&st);
+ FILETIME ft; SystemTimeToFileTime(&st,&ft);
+ WORD dosdate,dostime; FileTimeToDosDateTime(&ft,&dosdate,&dostime);
+ times.atime = filetime2timet(ft);
+ times.mtime = times.atime;
+ times.ctime = times.atime;
+ timestamp = (WORD)dostime | (((DWORD)dosdate)<<16);
+ hfin=hf;
+ return ZR_OK;
+ }
+}
+ZRESULT TZip::open_mem(void *src,unsigned int len)
+{ hfin=0; bufin=(const char*)src; selfclosehf=false; crc=CRCVAL_INITIAL; ired=0; csize=0; ired=0;
+ lenin=len; posin=0;
+ if (src==0 || len==0) return ZR_ARGS;
+ attr= 0x80000000; // just a normal file
+ isize = len;
+ iseekable=true;
+ SYSTEMTIME st; GetLocalTime(&st);
+ FILETIME ft; SystemTimeToFileTime(&st,&ft);
+ WORD dosdate,dostime; FileTimeToDosDateTime(&ft,&dosdate,&dostime);
+ times.atime = filetime2timet(ft);
+ times.mtime = times.atime;
+ times.ctime = times.atime;
+ timestamp = (WORD)dostime | (((DWORD)dosdate)<<16);
+ return ZR_OK;
+}
+ZRESULT TZip::open_dir()
+{ hfin=0; bufin=0; selfclosehf=false; crc=CRCVAL_INITIAL; isize=0; csize=0; ired=0;
+ attr= 0x41C00010; // a readable writable directory, and again directory
+ isize = 0;
+ iseekable=false;
+ SYSTEMTIME st; GetLocalTime(&st);
+ FILETIME ft; SystemTimeToFileTime(&st,&ft);
+ WORD dosdate,dostime; FileTimeToDosDateTime(&ft,&dosdate,&dostime);
+ times.atime = filetime2timet(ft);
+ times.mtime = times.atime;
+ times.ctime = times.atime;
+ timestamp = (WORD)dostime | (((DWORD)dosdate)<<16);
+ return ZR_OK;
+}
+
+unsigned TZip::sread(TState &s,char *buf,unsigned size)
+{ // static
+ TZip *zip = (TZip*)s.param;
+ return zip->read(buf,size);
+}
+
+unsigned TZip::read(char *buf, unsigned size)
+{ if (bufin!=0)
+ { if (posin>=lenin) return 0; // end of input
+ ulg red = lenin-posin;
+ if (red>size) red=size;
+ memcpy(buf, bufin+posin, red);
+ posin += red;
+ ired += red;
+ crc = crc32(crc, (uch*)buf, red);
+ return red;
+ }
+ else if (hfin!=0)
+ { DWORD red = 0;
+ BOOL ok = ReadFile(hfin,buf,size,&red,NULL);
+ if (!ok) return 0;
+ ired += red;
+ crc = crc32(crc, (uch*)buf, red);
+ return red;
+ }
+ else {oerr=ZR_NOTINITED; return 0;}
+}
+
+ZRESULT TZip::iclose()
+{ if (selfclosehf && hfin!=0) CloseHandle(hfin); hfin=0;
+ bool mismatch = (isize!=-1 && isize!=ired);
+ isize=ired; // and crc has been being updated anyway
+ if (mismatch) return ZR_MISSIZE;
+ else return ZR_OK;
+}
+
+
+
+ZRESULT TZip::ideflate(TZipFileInfo *zfi)
+{ TState state;
+ state.readfunc=sread; state.flush_outbuf=sflush;
+ state.param=this; state.level=8; state.seekable=iseekable; state.err=NULL;
+ // the following line will make ct_init realise it has to perform the init
+ state.ts.static_dtree[0].dl.len = 0;
+ // It would be nicer if I could figure out precisely which data had to
+ // be initted each time, and which didn't, but that's kind of difficult.
+ // Maybe for the next version...
+ //
+ bi_init(state,buf, sizeof(buf), TRUE); // it used to be just 1024-size, not 16384 as here
+ ct_init(state,&zfi->att);
+ lm_init(state,state.level, &zfi->flg);
+ ulg sz = deflate(state);
+ csize=sz;
+ if (state.err!=NULL) return ZR_FLATE;
+ else return ZR_OK;
+}
+
+ZRESULT TZip::istore()
+{ ulg size=0;
+ for (;;)
+ { unsigned int cin=read(buf,16384); if (cin<=0 || cin==(unsigned int)EOF) break;
+ unsigned int cout = write(buf,cin); if (cout!=cin) return ZR_MISSIZE;
+ size += cin;
+ }
+ csize=size;
+ return ZR_OK;
+}
+
+
+
+
+ZRESULT TZip::Add(const char *odstzn, void *src,unsigned int len, DWORD flags)
+{
+ if (oerr)
+ return ZR_FAILED;
+ if (hasputcen)
+ return ZR_ENDED;
+
+ // zip has its own notion of what its names should look like: i.e. dir/file.stuff
+ char dstzn[MAX_PATH] = {0};
+ strcpy(dstzn, odstzn);
+ if (*dstzn == 0)
+ return ZR_ARGS;
+ char *d=dstzn;
+ while (d && *d != 0)
+ {
+ if (*d == '\\')
+ *d = '/'; d++;
+ }
+ bool isdir = (flags==ZIP_FOLDER);
+ bool needs_trailing_slash = (isdir && dstzn[strlen(dstzn)-1]!='/');
+ int method=DEFLATE;
+ if (isdir || HasZipSuffix(dstzn))
+ method=STORE;
+
+ // now open whatever was our input source:
+ ZRESULT openres;
+ if (flags==ZIP_FILENAME)
+ openres=open_file((const TCHAR*)src);
+ else if (flags==ZIP_HANDLE)
+ openres=open_handle((HANDLE)src,len);
+ else if (flags==ZIP_MEMORY)
+ openres=open_mem(src,len);
+ else if (flags==ZIP_FOLDER)
+ openres=open_dir();
+ else return ZR_ARGS;
+ if (openres!=ZR_OK)
+ return openres;
+
+ // A zip "entry" consists of a local header (which includes the file name),
+ // then the compressed data, and possibly an extended local header.
+
+ // Initialize the local header
+ TZipFileInfo zfi; zfi.nxt=NULL;
+ strcpy(zfi.name,"");
+ strcpy(zfi.iname,dstzn);
+ zfi.nam=strlen(zfi.iname);
+ if (needs_trailing_slash)
+ {
+ strcat(zfi.iname,"/");
+ zfi.nam++;
+ }
+ strcpy(zfi.zname,"");
+ zfi.extra=NULL; zfi.ext=0; // extra header to go after this compressed data, and its length
+ zfi.cextra=NULL; zfi.cext=0; // extra header to go in the central end-of-zip directory, and its length
+ zfi.comment=NULL; zfi.com=0; // comment, and its length
+ zfi.mark = 1;
+ zfi.dosflag = 0;
+ zfi.att = (ush)BINARY;
+ zfi.vem = (ush)0xB17; // 0xB00 is win32 os-code. 0x17 is 23 in decimal: zip 2.3
+ zfi.ver = (ush)20; // Needs PKUNZIP 2.0 to unzip it
+ zfi.tim = timestamp;
+ // Even though we write the header now, it will have to be rewritten, since we don't know compressed size or crc.
+ zfi.crc = 0; // to be updated later
+ zfi.flg = 8; // 8 means 'there is an extra header'. Assume for the moment that we need it.
+ zfi.lflg = zfi.flg; // to be updated later
+ zfi.how = (ush)method; // to be updated later
+ zfi.siz = (ulg)(method==STORE && isize>=0 ? isize : 0); // to be updated later
+ zfi.len = (ulg)(isize); // to be updated later
+ zfi.dsk = 0;
+ zfi.atx = attr;
+ zfi.off = writ+ooffset; // offset within file of the start of this local record
+ // stuff the 'times' structure into zfi.extra
+ char xloc[EB_L_UT_SIZE] = {0};
+ zfi.extra=xloc;
+ zfi.ext=EB_L_UT_SIZE;
+ char xcen[EB_C_UT_SIZE] = {0};
+ zfi.cextra=xcen;
+ zfi.cext=EB_C_UT_SIZE;
+ xloc[0] = 'U';
+ xloc[1] = 'T';
+ xloc[2] = EB_UT_LEN(3); // length of data part of e.f.
+ xloc[3] = 0;
+ xloc[4] = EB_UT_FL_MTIME | EB_UT_FL_ATIME | EB_UT_FL_CTIME;
+ xloc[5] = (char)(times.mtime);
+ xloc[6] = (char)(times.mtime >> 8);
+ xloc[7] = (char)(times.mtime >> 16);
+ xloc[8] = (char)(times.mtime >> 24);
+ xloc[9] = (char)(times.atime);
+ xloc[10] = (char)(times.atime >> 8);
+ xloc[11] = (char)(times.atime >> 16);
+ xloc[12] = (char)(times.atime >> 24);
+ xloc[13] = (char)(times.ctime);
+ xloc[14] = (char)(times.ctime >> 8);
+ xloc[15] = (char)(times.ctime >> 16);
+ xloc[16] = (char)(times.ctime >> 24);
+ memcpy(zfi.cextra,zfi.extra,EB_C_UT_SIZE);
+ zfi.cextra[EB_LEN] = EB_UT_LEN(1);
+
+
+ // (1) Start by writing the local header:
+ int r = putlocal(&zfi,swrite,this);
+ if (r!=ZE_OK)
+ {
+ iclose();
+ return ZR_WRITE;
+ }
+ writ += 4 + LOCHEAD + (unsigned int)zfi.nam + (unsigned int)zfi.ext;
+ if (oerr!=ZR_OK)
+ {
+ iclose();
+ return oerr;
+ }
+
+ //(2) Write deflated/stored file to zip file
+ ZRESULT writeres=ZR_OK;
+ if (!isdir && method==DEFLATE)
+ writeres=ideflate(&zfi);
+ else if (!isdir && method==STORE)
+ writeres=istore();
+ else if (isdir)
+ csize=0;
+ iclose();
+ writ += csize;
+ if (oerr!=ZR_OK)
+ return oerr;
+ if (writeres!=ZR_OK)
+ return ZR_WRITE;
+
+ // (3) Either rewrite the local header with correct information...
+ bool first_header_has_size_right = (zfi.siz==csize);
+ zfi.crc = crc;
+ zfi.siz = csize;
+ zfi.len = isize;
+ if (ocanseek)
+ {
+ zfi.how = (ush)method;
+ if ((zfi.flg & 1) == 0)
+ zfi.flg &= ~8; // clear the extended local header flag
+ zfi.lflg = zfi.flg;
+ // rewrite the local header:
+ if (!oseek(zfi.off-ooffset))
+ return ZR_SEEK;
+ if ((r = putlocal(&zfi, swrite,this)) != ZE_OK)
+ return ZR_WRITE;
+ if (!oseek(writ))
+ return ZR_SEEK;
+ }
+ else
+ {
+ // (4) ... or put an updated header at the end
+ if (zfi.how != (ush) method)
+ return ZR_NOCHANGE;
+ if (method==STORE && !first_header_has_size_right)
+ return ZR_NOCHANGE;
+ if ((r = putextended(&zfi, swrite,this)) != ZE_OK)
+ return ZR_WRITE;
+ writ += 16L;
+ zfi.flg = zfi.lflg; // if flg modified by inflate, for the central index
+ }
+ if (oerr!=ZR_OK)
+ return oerr;
+
+ // Keep a copy of the zipfileinfo, for our end-of-zip directory
+ char *cextra = new char[zfi.cext];
+ memcpy(cextra,zfi.cextra,zfi.cext); zfi.cextra=cextra;
+ TZipFileInfo *pzfi = new TZipFileInfo;
+ memcpy(pzfi,&zfi,sizeof(zfi));
+ if (zfis==NULL)
+ zfis=pzfi;
+ else
+ {
+ TZipFileInfo *z=zfis;
+ while (z->nxt!=NULL)
+ z=z->nxt;
+ z->nxt=pzfi;
+ }
+ return ZR_OK;
+}
+
+ZRESULT TZip::AddCentral()
+{ // write central directory
+ int numentries = 0;
+ ulg pos_at_start_of_central = writ;
+ //ulg tot_unc_size=0, tot_compressed_size=0;
+ bool okay=true;
+ for (TZipFileInfo *zfi=zfis; zfi!=NULL; )
+ { if (okay)
+ { int res = putcentral(zfi, swrite,this);
+ if (res!=ZE_OK) okay=false;
+ }
+ writ += 4 + CENHEAD + (unsigned int)zfi->nam + (unsigned int)zfi->cext + (unsigned int)zfi->com;
+ //tot_unc_size += zfi->len;
+ //tot_compressed_size += zfi->siz;
+ numentries++;
+ //
+ TZipFileInfo *zfinext = zfi->nxt;
+ if (zfi->cextra!=0) delete[] zfi->cextra;
+ delete zfi;
+ zfi = zfinext;
+ }
+ ulg center_size = writ - pos_at_start_of_central;
+ if (okay)
+ { int res = putend(numentries, center_size, pos_at_start_of_central+ooffset, 0, NULL, swrite,this);
+ if (res!=ZE_OK) okay=false;
+ writ += 4 + ENDHEAD + 0;
+ }
+ if (!okay) return ZR_WRITE;
+ return ZR_OK;
+}
+
+
+
+
+
+ZRESULT lasterrorZ=ZR_OK;
+
+unsigned int FormatZipMessageZ(ZRESULT code, char *buf,unsigned int len)
+{ if (code==ZR_RECENT) code=lasterrorZ;
+ const char *msg="unknown zip result code";
+ switch (code)
+ { case ZR_OK: msg="Success"; break;
+ case ZR_NODUPH: msg="Culdn't duplicate handle"; break;
+ case ZR_NOFILE: msg="Couldn't create/open file"; break;
+ case ZR_NOALLOC: msg="Failed to allocate memory"; break;
+ case ZR_WRITE: msg="Error writing to file"; break;
+ case ZR_NOTFOUND: msg="File not found in the zipfile"; break;
+ case ZR_MORE: msg="Still more data to unzip"; break;
+ case ZR_CORRUPT: msg="Zipfile is corrupt or not a zipfile"; break;
+ case ZR_READ: msg="Error reading file"; break;
+ case ZR_ARGS: msg="Caller: faulty arguments"; break;
+ case ZR_PARTIALUNZ: msg="Caller: the file had already been partially unzipped"; break;
+ case ZR_NOTMMAP: msg="Caller: can only get memory of a memory zipfile"; break;
+ case ZR_MEMSIZE: msg="Caller: not enough space allocated for memory zipfile"; break;
+ case ZR_FAILED: msg="Caller: there was a previous error"; break;
+ case ZR_ENDED: msg="Caller: additions to the zip have already been ended"; break;
+ case ZR_ZMODE: msg="Caller: mixing creation and opening of zip"; break;
+ case ZR_NOTINITED: msg="Zip-bug: internal initialisation not completed"; break;
+ case ZR_SEEK: msg="Zip-bug: trying to seek the unseekable"; break;
+ case ZR_MISSIZE: msg="Zip-bug: the anticipated size turned out wrong"; break;
+ case ZR_NOCHANGE: msg="Zip-bug: tried to change mind, but not allowed"; break;
+ case ZR_FLATE: msg="Zip-bug: an internal error during flation"; break;
+ }
+ unsigned int mlen=(unsigned int)strlen(msg);
+ if (buf==0 || len==0) return mlen;
+ unsigned int n=mlen; if (n+1>len) n=len-1;
+ strncpy(buf,msg,n); buf[n]=0;
+ return mlen;
+}
+
+
+
+typedef struct
+{ DWORD flag;
+ TZip *zip;
+} TZipHandleData;
+
+
+HZIP CreateZipZ(void *z,unsigned int len,DWORD flags)
+{
+ _tzset();
+ TZip *zip = new TZip();
+ lasterrorZ = zip->Create(z,len,flags);
+ if (lasterrorZ != ZR_OK)
+ {
+ delete zip;
+ return 0;
+ }
+ TZipHandleData *han = new TZipHandleData;
+ han->flag = 2;
+ han->zip = zip;
+ return (HZIP)han;
+}
+
+ZRESULT ZipAdd(HZIP hz, const TCHAR *dstzn, void *src, unsigned int len, DWORD flags)
+{
+ if (hz == 0)
+ {
+ lasterrorZ = ZR_ARGS;
+ return ZR_ARGS;
+ }
+ TZipHandleData *han = (TZipHandleData*)hz;
+ if (han->flag != 2)
+ {
+ lasterrorZ = ZR_ZMODE;
+ return ZR_ZMODE;
+ }
+ TZip *zip = han->zip;
+
+
+ if (flags == ZIP_FILENAME)
+ {
+ char szDest[MAX_PATH*2];
+ memset(szDest, 0, sizeof(szDest));
+
+#ifdef _UNICODE
+ // need to convert Unicode dest to ANSI
+ int nActualChars = WideCharToMultiByte(CP_ACP, // code page
+ 0, // performance and mapping flags
+ (LPCWSTR) dstzn, // wide-character string
+ -1, // number of chars in string
+ szDest, // buffer for new string
+ MAX_PATH*2-2, // size of buffer
+ NULL, // default for unmappable chars
+ NULL); // set when default char used
+ if (nActualChars == 0)
+ return ZR_ARGS;
+#else
+ strcpy(szDest, dstzn);
+#endif
+
+ lasterrorZ = zip->Add(szDest, src, len, flags);
+ }
+ else
+ {
+ lasterrorZ = zip->Add((char *)dstzn, src, len, flags);
+ }
+
+ return lasterrorZ;
+}
+
+ZRESULT ZipGetMemory(HZIP hz, void **buf, unsigned long *len)
+{ if (hz==0) {if (buf!=0) *buf=0; if (len!=0) *len=0; lasterrorZ=ZR_ARGS;return ZR_ARGS;}
+ TZipHandleData *han = (TZipHandleData*)hz;
+ if (han->flag!=2) {lasterrorZ=ZR_ZMODE;return ZR_ZMODE;}
+ TZip *zip = han->zip;
+ lasterrorZ = zip->GetMemory(buf,len);
+ return lasterrorZ;
+}
+
+ZRESULT CloseZipZ(HZIP hz)
+{ if (hz==0) {lasterrorZ=ZR_ARGS;return ZR_ARGS;}
+ TZipHandleData *han = (TZipHandleData*)hz;
+ if (han->flag!=2) {lasterrorZ=ZR_ZMODE;return ZR_ZMODE;}
+ TZip *zip = han->zip;
+ lasterrorZ = zip->Close();
+ delete zip;
+ delete han;
+ return lasterrorZ;
+}
+
+bool IsZipHandleZ(HZIP hz)
+{ if (hz==0) return true;
+ TZipHandleData *han = (TZipHandleData*)hz;
+ return (han->flag==2);
+}
+
diff --git a/Src/Plugins/General/gen_crasher/feedback/xzip/XZip.h b/Src/Plugins/General/gen_crasher/feedback/xzip/XZip.h
new file mode 100644
index 00000000..9cc015b9
--- /dev/null
+++ b/Src/Plugins/General/gen_crasher/feedback/xzip/XZip.h
@@ -0,0 +1,324 @@
+// XZip.h Version 1.1
+//
+// Authors: Mark Adler et al. (see below)
+//
+// Modified by: Lucian Wischik
+// lu@wischik.com
+//
+// Version 1.0 - Turned C files into just a single CPP file
+// - Made them compile cleanly as C++ files
+// - Gave them simpler APIs
+// - Added the ability to zip/unzip directly in memory without
+// any intermediate files
+//
+// Modified by: Hans Dietrich
+// hdietrich2@hotmail.com
+//
+// Version 1.1: - Added Unicode support to CreateZip() and ZipAdd()
+// - Changed file names to avoid conflicts with Lucian's files
+//
+///////////////////////////////////////////////////////////////////////////////
+//
+// Lucian Wischik's comments:
+// --------------------------
+// THIS FILE is almost entirely based upon code by info-zip.
+// It has been modified by Lucian Wischik.
+// The original code may be found at http://www.info-zip.org
+// The original copyright text follows.
+//
+///////////////////////////////////////////////////////////////////////////////
+//
+// Original authors' comments:
+// ---------------------------
+// This is version 2002-Feb-16 of the Info-ZIP copyright and license. The
+// definitive version of this document should be available at
+// ftp://ftp.info-zip.org/pub/infozip/license.html indefinitely.
+//
+// Copyright (c) 1990-2002 Info-ZIP. All rights reserved.
+//
+// For the purposes of this copyright and license, "Info-ZIP" is defined as
+// the following set of individuals:
+//
+// Mark Adler, John Bush, Karl Davis, Harald Denker, Jean-Michel Dubois,
+// Jean-loup Gailly, Hunter Goatley, Ian Gorman, Chris Herborth, Dirk Haase,
+// Greg Hartwig, Robert Heath, Jonathan Hudson, Paul Kienitz,
+// David Kirschbaum, Johnny Lee, Onno van der Linden, Igor Mandrichenko,
+// Steve P. Miller, Sergio Monesi, Keith Owens, George Petrov, Greg Roelofs,
+// Kai Uwe Rommel, Steve Salisbury, Dave Smith, Christian Spieler,
+// Antoine Verheijen, Paul von Behren, Rich Wales, Mike White
+//
+// This software is provided "as is", without warranty of any kind, express
+// or implied. In no event shall Info-ZIP or its contributors be held liable
+// for any direct, indirect, incidental, special or consequential damages
+// arising out of the use of or inability to use this software.
+//
+// Permission is granted to anyone to use this software for any purpose,
+// including commercial applications, and to alter it and redistribute it
+// freely, subject to the following restrictions:
+//
+// 1. Redistributions of source code must retain the above copyright notice,
+// definition, disclaimer, and this list of conditions.
+//
+// 2. Redistributions in binary form (compiled executables) must reproduce
+// the above copyright notice, definition, disclaimer, and this list of
+// conditions in documentation and/or other materials provided with the
+// distribution. The sole exception to this condition is redistribution
+// of a standard UnZipSFX binary as part of a self-extracting archive;
+// that is permitted without inclusion of this license, as long as the
+// normal UnZipSFX banner has not been removed from the binary or disabled.
+//
+// 3. Altered versions--including, but not limited to, ports to new
+// operating systems, existing ports with new graphical interfaces, and
+// dynamic, shared, or static library versions--must be plainly marked
+// as such and must not be misrepresented as being the original source.
+// Such altered versions also must not be misrepresented as being
+// Info-ZIP releases--including, but not limited to, labeling of the
+// altered versions with the names "Info-ZIP" (or any variation thereof,
+// including, but not limited to, different capitalizations),
+// "Pocket UnZip", "WiZ" or "MacZip" without the explicit permission of
+// Info-ZIP. Such altered versions are further prohibited from
+// misrepresentative use of the Zip-Bugs or Info-ZIP e-mail addresses or
+// of the Info-ZIP URL(s).
+//
+// 4. Info-ZIP retains the right to use the names "Info-ZIP", "Zip", "UnZip",
+// "UnZipSFX", "WiZ", "Pocket UnZip", "Pocket Zip", and "MacZip" for its
+// own source and binary releases.
+//
+///////////////////////////////////////////////////////////////////////////////
+
+#ifndef XZIP_H
+#define XZIP_H
+
+// ZIP functions -- for creating zip files
+// This file is a repackaged form of the Info-Zip source code available
+// at www.info-zip.org. The original copyright notice may be found in
+// zip.cpp. The repackaging was done by Lucian Wischik to simplify its
+// use in Windows/C++.
+
+#ifndef XUNZIP_H
+DECLARE_HANDLE(HZIP); // An HZIP identifies a zip file that is being created
+#endif
+
+typedef DWORD ZRESULT; // result codes from any of the zip functions. Listed later.
+
+// flag values passed to some functions
+#define ZIP_HANDLE 1
+#define ZIP_FILENAME 2
+#define ZIP_MEMORY 3
+#define ZIP_FOLDER 4
+
+
+///////////////////////////////////////////////////////////////////////////////
+//
+// CreateZip()
+//
+// Purpose: Create a zip archive file
+//
+// Parameters: z - archive file name if flags is ZIP_FILENAME; for other
+// uses see below
+// len - for memory (ZIP_MEMORY) should be the buffer size;
+// for other uses, should be 0
+// flags - indicates usage, see below; for files, this will be
+// ZIP_FILENAME
+//
+// Returns: HZIP - non-zero if zip archive created ok, otherwise 0
+//
+HZIP CreateZip(void *z, unsigned int len, DWORD flags);
+// CreateZip - call this to start the creation of a zip file.
+// As the zip is being created, it will be stored somewhere:
+// to a pipe: CreateZip(hpipe_write, 0,ZIP_HANDLE);
+// in a file (by handle): CreateZip(hfile, 0,ZIP_HANDLE);
+// in a file (by name): CreateZip("c:\\test.zip", 0,ZIP_FILENAME);
+// in memory: CreateZip(buf, len,ZIP_MEMORY);
+// or in pagefile memory: CreateZip(0, len,ZIP_MEMORY);
+// The final case stores it in memory backed by the system paging file,
+// where the zip may not exceed len bytes. This is a bit friendlier than
+// allocating memory with new[]: it won't lead to fragmentation, and the
+// memory won't be touched unless needed.
+// Note: because pipes don't allow random access, the structure of a zipfile
+// created into a pipe is slightly different from that created into a file
+// or memory. In particular, the compressed-size of the item cannot be
+// stored in the zipfile until after the item itself. (Also, for an item added
+// itself via a pipe, the uncompressed-size might not either be known until
+// after.) This is not normally a problem. But if you try to unzip via a pipe
+// as well, then the unzipper will not know these things about the item until
+// after it has been unzipped. Therefore: for unzippers which don't just write
+// each item to disk or to a pipe, but instead pre-allocate memory space into
+// which to unzip them, then either you have to create the zip not to a pipe,
+// or you have to add items not from a pipe, or at least when adding items
+// from a pipe you have to specify the length.
+
+
+///////////////////////////////////////////////////////////////////////////////
+//
+// ZipAdd()
+//
+// Purpose: Add a file to a zip archive
+//
+// Parameters: hz - handle to an open zip archive
+// dstzn - name used inside the zip archive to identify the file
+// src - for a file (ZIP_FILENAME) this specifies the filename
+// to be added to the archive; for other uses, see below
+// len - for memory (ZIP_MEMORY) this specifies the buffer
+// length; for other uses, this should be 0
+// flags - indicates usage, see below; for files, this will be
+// ZIP_FILENAME
+//
+// Returns: ZRESULT - ZR_OK if success, otherwise some other value
+//
+ZRESULT ZipAdd(HZIP hz, const TCHAR *dstzn, void *src, unsigned int len, DWORD flags);
+// ZipAdd - call this for each file to be added to the zip.
+// dstzn is the name that the file will be stored as in the zip file.
+// The file to be added to the zip can come
+// from a pipe: ZipAdd(hz,"file.dat", hpipe_read,0,ZIP_HANDLE);
+// from a file: ZipAdd(hz,"file.dat", hfile,0,ZIP_HANDLE);
+// from a fname: ZipAdd(hz,"file.dat", "c:\\docs\\origfile.dat",0,ZIP_FILENAME);
+// from memory: ZipAdd(hz,"subdir\\file.dat", buf,len,ZIP_MEMORY);
+// (folder): ZipAdd(hz,"subdir", 0,0,ZIP_FOLDER);
+// Note: if adding an item from a pipe, and if also creating the zip file itself
+// to a pipe, then you might wish to pass a non-zero length to the ZipAdd
+// function. This will let the zipfile store the items size ahead of the
+// compressed item itself, which in turn makes it easier when unzipping the
+// zipfile into a pipe.
+
+
+///////////////////////////////////////////////////////////////////////////////
+//
+// CloseZip()
+//
+// Purpose: Close an open zip archive
+//
+// Parameters: hz - handle to an open zip archive
+//
+// Returns: ZRESULT - ZR_OK if success, otherwise some other value
+//
+ZRESULT CloseZip(HZIP hz);
+// CloseZip - the zip handle must be closed with this function.
+
+
+ZRESULT ZipGetMemory(HZIP hz, void **buf, unsigned long *len);
+// ZipGetMemory - If the zip was created in memory, via ZipCreate(0,ZIP_MEMORY),
+// then this function will return information about that memory block.
+// buf will receive a pointer to its start, and len its length.
+// Note: you can't add any more after calling this.
+
+
+unsigned int FormatZipMessage(ZRESULT code, char *buf,unsigned int len);
+// FormatZipMessage - given an error code, formats it as a string.
+// It returns the length of the error message. If buf/len points
+// to a real buffer, then it also writes as much as possible into there.
+
+
+
+// These are the result codes:
+#define ZR_OK 0x00000000 // nb. the pseudo-code zr-recent is never returned,
+#define ZR_RECENT 0x00000001 // but can be passed to FormatZipMessage.
+// The following come from general system stuff (e.g. files not openable)
+#define ZR_GENMASK 0x0000FF00
+#define ZR_NODUPH 0x00000100 // couldn't duplicate the handle
+#define ZR_NOFILE 0x00000200 // couldn't create/open the file
+#define ZR_NOALLOC 0x00000300 // failed to allocate some resource
+#define ZR_WRITE 0x00000400 // a general error writing to the file
+#define ZR_NOTFOUND 0x00000500 // couldn't find that file in the zip
+#define ZR_MORE 0x00000600 // there's still more data to be unzipped
+#define ZR_CORRUPT 0x00000700 // the zipfile is corrupt or not a zipfile
+#define ZR_READ 0x00000800 // a general error reading the file
+// The following come from mistakes on the part of the caller
+#define ZR_CALLERMASK 0x00FF0000
+#define ZR_ARGS 0x00010000 // general mistake with the arguments
+#define ZR_NOTMMAP 0x00020000 // tried to ZipGetMemory, but that only works on mmap zipfiles, which yours wasn't
+#define ZR_MEMSIZE 0x00030000 // the memory size is too small
+#define ZR_FAILED 0x00040000 // the thing was already failed when you called this function
+#define ZR_ENDED 0x00050000 // the zip creation has already been closed
+#define ZR_MISSIZE 0x00060000 // the indicated input file size turned out mistaken
+#define ZR_PARTIALUNZ 0x00070000 // the file had already been partially unzipped
+#define ZR_ZMODE 0x00080000 // tried to mix creating/opening a zip
+// The following come from bugs within the zip library itself
+#define ZR_BUGMASK 0xFF000000
+#define ZR_NOTINITED 0x01000000 // initialisation didn't work
+#define ZR_SEEK 0x02000000 // trying to seek in an unseekable file
+#define ZR_NOCHANGE 0x04000000 // changed its mind on storage, but not allowed
+#define ZR_FLATE 0x05000000 // an internal error in the de/inflation code
+
+
+
+// e.g.
+//
+// (1) Traditional use, creating a zipfile from existing files
+// HZIP hz = CreateZip("c:\\temp.zip",0,ZIP_FILENAME);
+// ZipAdd(hz,"src1.txt", "c:\\src1.txt",0,ZIP_FILENAME);
+// ZipAdd(hz,"src2.bmp", "c:\\src2_origfn.bmp",0,ZIP_FILENAME);
+// CloseZip(hz);
+//
+// (2) Memory use, creating an auto-allocated mem-based zip file from various sources
+// HZIP hz = CreateZip(0,100000,ZIP_MEMORY);
+// // adding a conventional file...
+// ZipAdd(hz,"src1.txt", "c:\\src1.txt",0,ZIP_FILENAME);
+// // adding something from memory...
+// char buf[1000]; for (int i=0; i<1000; i++) buf[i]=(char)(i&0x7F);
+// ZipAdd(hz,"file.dat", buf,1000,ZIP_MEMORY);
+// // adding something from a pipe...
+// HANDLE hread,hwrite; CreatePipe(&hread,&write,NULL,0);
+// HANDLE hthread = CreateThread(ThreadFunc,(void*)hwrite);
+// ZipAdd(hz,"unz3.dat", hread,0,ZIP_HANDLE);
+// WaitForSingleObject(hthread,INFINITE);
+// CloseHandle(hthread); CloseHandle(hread);
+// ... meanwhile DWORD CALLBACK ThreadFunc(void *dat)
+// { HANDLE hwrite = (HANDLE)dat;
+// char buf[1000]={17};
+// DWORD writ; WriteFile(hwrite,buf,1000,&writ,NULL);
+// CloseHandle(hwrite);
+// return 0;
+// }
+// // and now that the zip is created, let's do something with it:
+// void *zbuf; unsigned long zlen; ZipGetMemory(hz,&zbuf,&zlen);
+// HANDLE hfz = CreateFile("test2.zip",GENERIC_WRITE,CREATE_ALWAYS);
+// DWORD writ; WriteFile(hfz,zbuf,zlen,&writ,NULL);
+// CloseHandle(hfz);
+// CloseZip(hz);
+//
+// (3) Handle use, for file handles and pipes
+// HANDLE hzread,hzwrite; CreatePipe(&hzread,&hzwrite);
+// HANDLE hthread = CreateThread(ZipReceiverThread,(void*)hread);
+// HZIP hz = ZipCreate(hzwrite,ZIP_HANDLE);
+// // ... add to it
+// CloseZip(hz);
+// CloseHandle(hzwrite);
+// WaitForSingleObject(hthread,INFINITE);
+// CloseHandle(hthread);
+// ... meanwhile DWORD CALLBACK ThreadFunc(void *dat)
+// { HANDLE hread = (HANDLE)dat;
+// char buf[1000] = {0};
+// while (true)
+// { DWORD red = 0; ReadFile(hread,buf,1000,&red,NULL);
+// // ... and do something with this zip data we're receiving
+// if (red==0) break;
+// }
+// CloseHandle(hread);
+// return 0;
+// }
+//
+
+
+// Now we indulge in a little skullduggery so that the code works whether
+// the user has included just zip or both zip and unzip.
+// Idea: if header files for both zip and unzip are present, then presumably
+// the cpp files for zip and unzip are both present, so we will call
+// one or the other of them based on a dynamic choice. If the header file
+// for only one is present, then we will bind to that particular one.
+HZIP CreateZipZ(void *z,unsigned int len,DWORD flags);
+ZRESULT CloseZipZ(HZIP hz);
+unsigned int FormatZipMessageZ(ZRESULT code, char *buf,unsigned int len);
+bool IsZipHandleZ(HZIP hz);
+#define CreateZip CreateZipZ
+
+#ifdef XUNZIP_H
+#undef CloseZip
+#define CloseZip(hz) (IsZipHandleZ(hz)?CloseZipZ(hz):CloseZipU(hz))
+#else
+#define CloseZip CloseZipZ
+#define FormatZipMessage FormatZipMessageZ
+#endif
+
+
+#endif //XZIP_H
diff --git a/Src/Plugins/General/gen_crasher/gen_crasher.rc b/Src/Plugins/General/gen_crasher/gen_crasher.rc
new file mode 100644
index 00000000..f989782e
--- /dev/null
+++ b/Src/Plugins/General/gen_crasher/gen_crasher.rc
@@ -0,0 +1,193 @@
+// Microsoft Visual C++ generated resource script.
+//
+#include "resource.h"
+
+#define APSTUDIO_READONLY_SYMBOLS
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 2 resource.
+//
+#include "afxres.h"
+
+/////////////////////////////////////////////////////////////////////////////
+#undef APSTUDIO_READONLY_SYMBOLS
+
+/////////////////////////////////////////////////////////////////////////////
+// English (U.S.) resources
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
+#ifdef _WIN32
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
+#pragma code_page(1252)
+#endif //_WIN32
+
+#ifdef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// TEXTINCLUDE
+//
+
+1 TEXTINCLUDE
+BEGIN
+ "resource.h\0"
+END
+
+2 TEXTINCLUDE
+BEGIN
+ "#include ""afxres.h""\r\n"
+ "\0"
+END
+
+3 TEXTINCLUDE
+BEGIN
+ "#include ""version.rc2""\r\n"
+ "\0"
+END
+
+#endif // APSTUDIO_INVOKED
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Dialog
+//
+
+IDD_CRASHDLG DIALOGEX 0, 0, 187, 42
+STYLE DS_SYSMODAL | DS_SETFONT | DS_SETFOREGROUND | DS_FIXEDSYS | DS_NOFAILCREATE | DS_CENTER | WS_POPUP | WS_CAPTION | WS_SYSMENU
+EXSTYLE WS_EX_NOPARENTNOTIFY
+CAPTION "Winamp Error Reporter"
+FONT 8, "MS Shell Dlg", 400, 0, 0x1
+BEGIN
+ ICON 102,IDC_BMP_LOGO,8,6,21,20,SS_REALSIZEIMAGE
+ LTEXT "",IDC_LBL_STEP,48,6,127,10
+ CONTROL "",IDC_PRG_COLLECT,"msctls_progress32",WS_BORDER,48,19,127,9
+END
+
+IDD_CONFIG DIALOGEX 0, 0, 273, 246
+STYLE DS_SETFONT | DS_FIXEDSYS | DS_CONTROL | WS_CHILD
+EXSTYLE WS_EX_CONTROLPARENT
+FONT 8, "MS Shell Dlg", 400, 0, 0x1
+BEGIN
+ GROUPBOX "General",IDC_GRP_GENERAL,0,0,273,64
+ CONTROL "Auto Restart",IDC_CHK_RESTART,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,6,12,90,10
+ CONTROL "Compress results",IDC_CHK_COMPRESS,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,6,24,90,10
+ CONTROL "Create Dump File",IDC_CHK_CREATEDMP,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,6,36,90,10
+ CONTROL "Create Log File",IDC_CHK_CREATELOG,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,6,49,90,10
+ GROUPBOX "",IDC_GRP_EMAIL,101,13,165,45
+ CONTROL "Send Data",IDC_CHK_SEND,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,106,12,46,10
+ CONTROL "Using default email program",IDC_RB_USECLIENT,"Button",BS_AUTORADIOBUTTON,106,27,105,10
+ CONTROL "Using SMTP server",IDC_RB_USESMTP,"Button",BS_AUTORADIOBUTTON,106,41,75,10
+ PUSHBUTTON "SMTP Settings...",IDC_BTN_SMTP,185,39,75,13
+ GROUPBOX "Dump File",IDC_GRP_DUMP,0,67,273,66
+ LTEXT "OS version:",IDC_LBL_OSVERSION_CAPTION,6,78,38,8
+ LTEXT "",IDC_LBL_OSVERSION,48,78,218,8
+ LTEXT "Dll path:",IDC_LBL_DLLPATH_CAPTION,6,90,38,8
+ LTEXT "",IDC_LBL_DLLPATH,48,90,218,8,SS_PATHELLIPSIS
+ LTEXT "Dll version:",IDC_LBL_DLLVERSION_CAPTION,6,102,38,8
+ LTEXT "unknown [unable to load]",IDC_LBL_DLLVERSION,48,102,219,8
+ LTEXT "Type:",IDC_LBL_DMPTYPE,6,116,20,8
+ COMBOBOX IDC_CMB_DMPTYPE,30,114,237,84,CBS_DROPDOWNLIST | CBS_SORT | WS_VSCROLL | WS_TABSTOP
+ GROUPBOX "Log File",IDC_GRP_LOG,0,136,273,28
+ CONTROL "System info",IDC_CHK_LOGSYSTEM,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,6,149,53,10
+ CONTROL "Stack data",IDC_CHK_LOGSTACK,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,63,149,50,10
+ CONTROL "Registry state",IDC_CHK_LOGREGISTRY,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,117,149,62,10
+ CONTROL "Loaded modules",IDC_CHK_LOGMODULE,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,183,149,67,10
+ LTEXT "Save report in:",IDC_LBL_PATH,6,180,50,8
+ EDITTEXT IDC_EDT_PATH,60,178,188,12,ES_AUTOHSCROLL
+ PUSHBUTTON "...",IDC_BTN_PATH,249,178,19,12
+ GROUPBOX "File Paths",IDC_GRP_ZIP,1,168,272,76
+ LTEXT "Zip filename:",IDC_LBL_ZIPNAME,6,196,50,8
+ EDITTEXT IDC_EDT_ZIPNAME,60,194,208,12,ES_AUTOHSCROLL
+ LTEXT "Dump filename:",IDC_LBL_DMPNAME,6,212,50,8
+ EDITTEXT IDC_EDT_DMPNAME,60,210,208,12,ES_AUTOHSCROLL
+ LTEXT "Log filename:",IDC_LBL_LOGNAME,6,229,50,8
+ EDITTEXT IDC_EDT_LOGNAME,60,227,208,12,ES_AUTOHSCROLL
+ CONTROL "Do not ask questions",IDC_CHK_SILENT,"Button",BS_AUTOCHECKBOX | NOT WS_VISIBLE | WS_DISABLED | WS_TABSTOP,281,0,90,10
+END
+
+IDD_DLG_SMTP DIALOGEX 0, 0, 180, 155
+STYLE DS_SETFONT | DS_SETFOREGROUND | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU
+EXSTYLE WS_EX_TOOLWINDOW
+CAPTION "SMTP Settings"
+FONT 8, "MS Shell Dlg", 400, 0, 0x1
+BEGIN
+ GROUPBOX "Server Details",IDC_GRP_AUTH2,5,5,170,63,BS_LEFT
+ LTEXT "Server:",IDC_LBL_SERVER,12,18,54,8
+ EDITTEXT IDC_EDT_SERVER,70,16,100,12,ES_AUTOHSCROLL
+ LTEXT "Port:",IDC_LBL_PORT,12,33,54,8
+ EDITTEXT IDC_EDT_PORT,70,32,21,12,ES_AUTOHSCROLL | ES_NUMBER,WS_EX_RIGHT
+ LTEXT "Sender Address:",IDC_LBL_ADDRESS,12,50,54,8
+ EDITTEXT IDC_EDT_ADDRESS,70,48,100,14,ES_AUTOHSCROLL
+ GROUPBOX "Authentication",IDC_GRP_AUTH,5,71,170,62,BS_LEFT
+ CONTROL "Server requires authentication",IDC_CHK_AUTH,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,11,84,116,9
+ LTEXT "User:",IDC_LBL_USER,11,100,34,8
+ EDITTEXT IDC_EDT_USER,49,97,120,12,ES_AUTOHSCROLL
+ LTEXT "Password:",IDC_LBL_PWD,11,116,34,8
+ EDITTEXT IDC_EDT_PWD,49,113,120,12,ES_PASSWORD | ES_AUTOHSCROLL
+ DEFPUSHBUTTON "Close",IDCANCEL,125,137,50,13
+END
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// DESIGNINFO
+//
+
+#ifdef APSTUDIO_INVOKED
+GUIDELINES DESIGNINFO
+BEGIN
+ IDD_CRASHDLG, DIALOG
+ BEGIN
+ BOTTOMMARGIN, 39
+ END
+
+ IDD_DLG_SMTP, DIALOG
+ BEGIN
+ LEFTMARGIN, 5
+ RIGHTMARGIN, 175
+ TOPMARGIN, 5
+ BOTTOMMARGIN, 150
+ END
+END
+#endif // APSTUDIO_INVOKED
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// String Table
+//
+
+STRINGTABLE
+BEGIN
+ IDS_ERROR_FEEDBACK "Error Feedback"
+ IDS_UNKNOWN "unknown"
+ IDS_LOADED_OK "loaded"
+ IDS_UNABLE_TO_LOAD "unable to load"
+ IDS_NOT_FOUND "not found"
+ IDS_UNABLE_TO_SAVE_SETTINGS "Unable to save error feedback settings"
+ IDS_SAVE_ERROR "Save Error"
+ IDS_SELECT_FOLDER_FOR_ERROR_INFO
+ "Select folder where the error information will be saved to"
+END
+
+STRINGTABLE
+BEGIN
+ IDS_NULLSOFT_ERROR_FEEDBACK "Nullsoft Error Feedback v%s"
+ 65535 "{092A97EF-7DC0-41a7-80D1-90DEEB18F12D}"
+END
+
+#endif // English (U.S.) resources
+/////////////////////////////////////////////////////////////////////////////
+
+
+
+#ifndef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 3 resource.
+//
+#include "version.rc2"
+
+/////////////////////////////////////////////////////////////////////////////
+#endif // not APSTUDIO_INVOKED
+
diff --git a/Src/Plugins/General/gen_crasher/gen_crasher.sln b/Src/Plugins/General/gen_crasher/gen_crasher.sln
new file mode 100644
index 00000000..e849e1e7
--- /dev/null
+++ b/Src/Plugins/General/gen_crasher/gen_crasher.sln
@@ -0,0 +1,31 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 16
+VisualStudioVersion = 16.0.29424.173
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "gen_crasher", "gen_crasher.vcxproj", "{A029F791-2838-4D16-BC92-C8E04D677948}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Win32 = Debug|Win32
+ Debug|x64 = Debug|x64
+ Release|Win32 = Release|Win32
+ Release|x64 = Release|x64
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {A029F791-2838-4D16-BC92-C8E04D677948}.Debug|Win32.ActiveCfg = Debug|Win32
+ {A029F791-2838-4D16-BC92-C8E04D677948}.Debug|Win32.Build.0 = Debug|Win32
+ {A029F791-2838-4D16-BC92-C8E04D677948}.Debug|x64.ActiveCfg = Debug|x64
+ {A029F791-2838-4D16-BC92-C8E04D677948}.Debug|x64.Build.0 = Debug|x64
+ {A029F791-2838-4D16-BC92-C8E04D677948}.Release|Win32.ActiveCfg = Release|Win32
+ {A029F791-2838-4D16-BC92-C8E04D677948}.Release|Win32.Build.0 = Release|Win32
+ {A029F791-2838-4D16-BC92-C8E04D677948}.Release|x64.ActiveCfg = Release|x64
+ {A029F791-2838-4D16-BC92-C8E04D677948}.Release|x64.Build.0 = Release|x64
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {A8CBA02D-A7C5-4784-BC88-C554B8121167}
+ EndGlobalSection
+EndGlobal
diff --git a/Src/Plugins/General/gen_crasher/gen_crasher.vcxproj b/Src/Plugins/General/gen_crasher/gen_crasher.vcxproj
new file mode 100644
index 00000000..5412bea0
--- /dev/null
+++ b/Src/Plugins/General/gen_crasher/gen_crasher.vcxproj
@@ -0,0 +1,290 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Debug|Win32">
+ <Configuration>Debug</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Debug|x64">
+ <Configuration>Debug</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|Win32">
+ <Configuration>Release</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|x64">
+ <Configuration>Release</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <ProjectGuid>{A029F791-2838-4D16-BC92-C8E04D677948}</ProjectGuid>
+ <RootNamespace>gen_crasher</RootNamespace>
+ <WindowsTargetPlatformVersion>10.0.19041.0</WindowsTargetPlatformVersion>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+ <ConfigurationType>DynamicLibrary</ConfigurationType>
+ <PlatformToolset>v142</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
+ <ConfigurationType>DynamicLibrary</ConfigurationType>
+ <PlatformToolset>v142</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+ <ConfigurationType>DynamicLibrary</ConfigurationType>
+ <PlatformToolset>v142</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
+ <ConfigurationType>DynamicLibrary</ConfigurationType>
+ <PlatformToolset>v142</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>$(PlatformShortName)_$(Configuration)\</OutDir>
+ <IntDir>$(PlatformShortName)_$(Configuration)\</IntDir>
+ <IncludePath>$(IncludePath)</IncludePath>
+ <LibraryPath>$(LibraryPath)</LibraryPath>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>$(PlatformShortName)_$(Configuration)\</OutDir>
+ <IntDir>$(PlatformShortName)_$(Configuration)\</IntDir>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>$(PlatformShortName)_$(Configuration)\</OutDir>
+ <IntDir>$(PlatformShortName)_$(Configuration)\</IntDir>
+ <IncludePath>$(IncludePath)</IncludePath>
+ <LibraryPath>$(LibraryPath)</LibraryPath>
+ <EmbedManifest>true</EmbedManifest>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>$(PlatformShortName)_$(Configuration)\</OutDir>
+ <IntDir>$(PlatformShortName)_$(Configuration)\</IntDir>
+ </PropertyGroup>
+ <PropertyGroup Label="Vcpkg">
+ <VcpkgEnabled>false</VcpkgEnabled>
+ </PropertyGroup>
+ <PropertyGroup Label="Vcpkg" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <VcpkgConfiguration>Debug</VcpkgConfiguration>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <ClCompile>
+ <Optimization>Disabled</Optimization>
+ <AdditionalIncludeDirectories>..\..\..\Wasabi;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>_WIN32_WINNT=0x0601;WINVER=0x0601;WIN32;_DEBUG;_WINDOWS;_USRDLL;GEN_CRASHER_EXPORTS;_WIN32_IE=0x0A00;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <MinimalRebuild>false</MinimalRebuild>
+ <MultiProcessorCompilation>true</MultiProcessorCompilation>
+ <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
+ <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
+ <TreatWChar_tAsBuiltInType>false</TreatWChar_tAsBuiltInType>
+ <WarningLevel>Level3</WarningLevel>
+ <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
+ <DisableSpecificWarnings>4996;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ <BufferSecurityCheck>false</BufferSecurityCheck>
+ <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>shlwapi.lib;Version.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <ProgramDatabaseFile>$(IntDir)$(TargetName).pdb</ProgramDatabaseFile>
+ <SubSystem>Windows</SubSystem>
+ <RandomizedBaseAddress>false</RandomizedBaseAddress>
+ <ImportLibrary>$(IntDir)$(TargetName).lib</ImportLibrary>
+ <TargetMachine>MachineX86</TargetMachine>
+ <ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>
+ <AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+ </Link>
+ <PostBuildEvent>
+ <Command>xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\
+xcopy /Y /D $(IntDir)$(TargetName).pdb ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\ </Command>
+ <Message>Post build event: 'xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\'</Message>
+ </PostBuildEvent>
+ <ResourceCompile>
+ <PreprocessorDefinitions>_WIN32_WINNT=0x0601;WINVER=0x0601;_UNICODE;UNICODE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ </ResourceCompile>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <ClCompile>
+ <Optimization>Disabled</Optimization>
+ <AdditionalIncludeDirectories>..\..\..\Wasabi;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>_WIN32_WINNT=0x0601;WINVER=0x0601;_DEBUG;WIN64;_WINDOWS;_USRDLL;GEN_CRASHER_EXPORTS;_WIN32_IE=0x0A00;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <MinimalRebuild>false</MinimalRebuild>
+ <MultiProcessorCompilation>true</MultiProcessorCompilation>
+ <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
+ <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
+ <BrowseInformation>true</BrowseInformation>
+ <WarningLevel>Level3</WarningLevel>
+ <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
+ <CompileAs>Default</CompileAs>
+ <DisableSpecificWarnings>4996;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ <BufferSecurityCheck>false</BufferSecurityCheck>
+ <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>shlwapi.lib;Version.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <ProgramDatabaseFile>$(IntDir)$(TargetName).pdb</ProgramDatabaseFile>
+ <RandomizedBaseAddress>false</RandomizedBaseAddress>
+ <ImportLibrary>$(IntDir)$(TargetName).lib</ImportLibrary>
+ <ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>
+ <AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+ <SubSystem>Windows</SubSystem>
+ </Link>
+ <PostBuildEvent>
+ <Command>xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\
+xcopy /Y /D $(IntDir)$(TargetName).pdb ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\ </Command>
+ <Message>Post build event: 'xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\'</Message>
+ </PostBuildEvent>
+ <ResourceCompile>
+ <PreprocessorDefinitions>_WIN32_WINNT=0x0601;WINVER=0x0601;_UNICODE;UNICODE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ </ResourceCompile>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <ClCompile>
+ <Optimization>MinSpace</Optimization>
+ <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion>
+ <FavorSizeOrSpeed>Size</FavorSizeOrSpeed>
+ <AdditionalIncludeDirectories>..\..\..\Wasabi;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>_WIN32_WINNT=0x0601;WINVER=0x0601;WIN32;NDEBUG;_WINDOWS;_USRDLL;GEN_CRASHER_EXPORTS;_WIN32_IE=0x0A00;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <StringPooling>true</StringPooling>
+ <MultiProcessorCompilation>true</MultiProcessorCompilation>
+ <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
+ <BufferSecurityCheck>false</BufferSecurityCheck>
+ <ObjectFileName>$(IntDir)</ObjectFileName>
+ <WarningLevel>Level3</WarningLevel>
+ <DebugInformationFormat>None</DebugInformationFormat>
+ <DisableSpecificWarnings>4996;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>shlwapi.lib;Version.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
+ <GenerateDebugInformation>false</GenerateDebugInformation>
+ <ProgramDatabaseFile>$(IntDir)$(TargetName).pdb</ProgramDatabaseFile>
+ <SubSystem>Windows</SubSystem>
+ <OptimizeReferences>true</OptimizeReferences>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <RandomizedBaseAddress>false</RandomizedBaseAddress>
+ <TurnOffAssemblyGeneration>true</TurnOffAssemblyGeneration>
+ <ImportLibrary>$(IntDir)$(TargetName).lib</ImportLibrary>
+ <TargetMachine>MachineX86</TargetMachine>
+ <ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>
+ <AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+ </Link>
+ <PostBuildEvent>
+ <Command>xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\ </Command>
+ <Message>Post build event: 'xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\'</Message>
+ </PostBuildEvent>
+ <ResourceCompile>
+ <PreprocessorDefinitions>_WIN32_WINNT=0x0601;WINVER=0x0601;_UNICODE;UNICODE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ </ResourceCompile>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <ClCompile>
+ <Optimization>MinSpace</Optimization>
+ <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion>
+ <FavorSizeOrSpeed>Size</FavorSizeOrSpeed>
+ <AdditionalIncludeDirectories>..\..\..\Wasabi;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>_WIN32_WINNT=0x0601;WINVER=0x0601;NDEBUG;WIN64;_WINDOWS;_USRDLL;GEN_CRASHER_EXPORTS;_WIN32_IE=0x0A00;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <StringPooling>true</StringPooling>
+ <MultiProcessorCompilation>true</MultiProcessorCompilation>
+ <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
+ <BufferSecurityCheck>false</BufferSecurityCheck>
+ <ObjectFileName>$(IntDir)</ObjectFileName>
+ <WarningLevel>Level3</WarningLevel>
+ <DebugInformationFormat>None</DebugInformationFormat>
+ <DisableSpecificWarnings>4996;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>shlwapi.lib;Version.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
+ <GenerateDebugInformation>false</GenerateDebugInformation>
+ <ProgramDatabaseFile>$(IntDir)$(TargetName).pdb</ProgramDatabaseFile>
+ <OptimizeReferences>true</OptimizeReferences>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <RandomizedBaseAddress>false</RandomizedBaseAddress>
+ <TurnOffAssemblyGeneration>true</TurnOffAssemblyGeneration>
+ <ImportLibrary>$(IntDir)$(TargetName).lib</ImportLibrary>
+ <ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>
+ <AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+ <SubSystem>Windows</SubSystem>
+ </Link>
+ <PostBuildEvent>
+ <Command>xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\ </Command>
+ <Message>Post build event: 'xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\'</Message>
+ </PostBuildEvent>
+ <ResourceCompile>
+ <PreprocessorDefinitions>_WIN32_WINNT=0x0601;WINVER=0x0601;_UNICODE;UNICODE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ </ResourceCompile>
+ </ItemDefinitionGroup>
+ <ItemGroup>
+ <ProjectReference Include="..\..\..\Wasabi\Wasabi.vcxproj">
+ <Project>{3e0bfa8a-b86a-42e9-a33f-ec294f823f7f}</Project>
+ </ProjectReference>
+ <ProjectReference Include="feedback\feedback.vcxproj">
+ <Project>{a845d04c-a95e-424c-bfa8-d7706dba78bf}</Project>
+ <CopyLocalSatelliteAssemblies>true</CopyLocalSatelliteAssemblies>
+ <ReferenceOutputAssembly>true</ReferenceOutputAssembly>
+ </ProjectReference>
+ </ItemGroup>
+ <ItemGroup>
+ <ClCompile Include="..\..\..\nu\ServiceWatcher.cpp" />
+ <ClCompile Include="config.cpp" />
+ <ClCompile Include="configDlg.cpp" />
+ <ClCompile Include="crashDlg.cpp" />
+ <ClCompile Include="ExceptionHandler.cpp" />
+ <ClCompile Include="GetWinVer.cpp" />
+ <ClCompile Include="main.cpp" />
+ <ClCompile Include="MiniVersion.cpp" />
+ <ClCompile Include="settings.cpp" />
+ <ClCompile Include="smtpDlg.cpp" />
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="..\..\..\Winamp\wa_ipc.h" />
+ <ClInclude Include="api__gen_crasher.h" />
+ <ClInclude Include="config.h" />
+ <ClInclude Include="configDlg.h" />
+ <ClInclude Include="crashDlg.h" />
+ <ClInclude Include="ExceptionHandler.h" />
+ <ClInclude Include="GetWinVer.h" />
+ <ClInclude Include="main.h" />
+ <ClInclude Include="minidump.h" />
+ <ClInclude Include="MiniVersion.h" />
+ <ClInclude Include="resource.h" />
+ <ClInclude Include="settings.h" />
+ <ClInclude Include="smtpDlg.h" />
+ </ItemGroup>
+ <ItemGroup>
+ <ResourceCompile Include="gen_crasher.rc" />
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project> \ No newline at end of file
diff --git a/Src/Plugins/General/gen_crasher/gen_crasher.vcxproj.filters b/Src/Plugins/General/gen_crasher/gen_crasher.vcxproj.filters
new file mode 100644
index 00000000..a1cf4506
--- /dev/null
+++ b/Src/Plugins/General/gen_crasher/gen_crasher.vcxproj.filters
@@ -0,0 +1,92 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup>
+ <ClCompile Include="config.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="configDlg.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="crashDlg.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="ExceptionHandler.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="GetWinVer.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="main.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="MiniVersion.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="settings.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="smtpDlg.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\nu\ServiceWatcher.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="api__gen_crasher.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="config.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="configDlg.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="crashDlg.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="ExceptionHandler.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="GetWinVer.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="main.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="minidump.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="MiniVersion.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="resource.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="settings.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="smtpDlg.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\Winamp\wa_ipc.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ </ItemGroup>
+ <ItemGroup>
+ <Filter Include="Header Files">
+ <UniqueIdentifier>{387133cc-523c-4c1f-8215-3de0d29784a3}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Ressource Files">
+ <UniqueIdentifier>{1515be17-59cd-45ca-abc3-a3b3e66c36b9}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Source Files">
+ <UniqueIdentifier>{89f5adbb-9d33-4588-b2cb-942e0cc75987}</UniqueIdentifier>
+ </Filter>
+ </ItemGroup>
+ <ItemGroup>
+ <ResourceCompile Include="gen_crasher.rc">
+ <Filter>Ressource Files</Filter>
+ </ResourceCompile>
+ </ItemGroup>
+</Project> \ No newline at end of file
diff --git a/Src/Plugins/General/gen_crasher/main.cpp b/Src/Plugins/General/gen_crasher/main.cpp
new file mode 100644
index 00000000..7385db07
--- /dev/null
+++ b/Src/Plugins/General/gen_crasher/main.cpp
@@ -0,0 +1,140 @@
+// Winamp error feedback plugin
+// Copyright (C) 2005 Nullsoft
+
+//#define PLUGIN_DESC "Nullsoft Error Feedback"
+#define PLUGIN_VER L"1.16"
+
+#include ".\main.h"
+#include "configDlg.h"
+#include "crashDlg.h"
+#include "api__gen_crasher.h"
+#include "../nu/ServiceWatcher.h"
+
+ServiceWatcher watcher;
+Settings settings;
+prefsDlgRecW prefItem = {0};
+char *winampVersion;
+static wchar_t prefsTitle[64];
+
+// wasabi based services for localisation support
+api_service *WASABI_API_SVC = 0;
+api_language *WASABI_API_LNG = 0;
+api_syscb *WASABI_API_SYSCB = 0;
+api_application *WASABI_API_APP = 0;
+HINSTANCE WASABI_API_LNG_HINST = 0, WASABI_API_ORIG_HINST = 0;
+
+int init(void);
+void config(void);
+void quit(void);
+
+extern "C" winampGeneralPurposePlugin plugin =
+{
+ GPPHDR_VER_U,
+ "nullsoft(gen_crasher.dll)",
+ init,
+ config,
+ quit,
+};
+
+extern "C" __declspec(dllexport) winampGeneralPurposePlugin * winampGetGeneralPurposePlugin() { return &plugin; }
+
+int init(void)
+{
+ if (!settings.IsOk()) return GEN_INIT_FAILURE;
+
+ // loader so that we can get the localisation service api for use
+ WASABI_API_SVC = (api_service*)SendMessage(plugin.hwndParent, WM_WA_IPC, 0, IPC_GET_API_SERVICE);
+ if (!WASABI_API_SVC || WASABI_API_SVC == (api_service *)1)
+ return GEN_INIT_FAILURE;
+
+ waServiceFactory *sf = WASABI_API_SVC->service_getServiceByGuid(applicationApiServiceGuid);
+ if (sf) WASABI_API_SYSCB = reinterpret_cast<api_syscb*>(sf->getInterface());
+
+ watcher.WatchWith(WASABI_API_SVC);
+ watcher.WatchFor(&WASABI_API_LNG, languageApiGUID);
+ WASABI_API_SYSCB->syscb_registerCallback(&watcher);
+
+ sf = WASABI_API_SVC->service_getServiceByGuid(applicationApiServiceGuid);
+ if (sf) WASABI_API_APP = reinterpret_cast<api_application*>(sf->getInterface());
+
+ // need to have this initialised before we try to do anything with localisation features
+ WASABI_API_START_LANG(plugin.hDllInstance,GenCrasherLangGUID);
+
+ static wchar_t szDescription[256];
+ swprintf(szDescription, ARRAYSIZE(szDescription),
+ WASABI_API_LNGSTRINGW(IDS_NULLSOFT_ERROR_FEEDBACK), PLUGIN_VER);
+ plugin.description = (char*)szDescription;
+
+ //register prefs screen
+ prefItem.dlgID = IDD_CONFIG;
+ prefItem.name = WASABI_API_LNGSTRINGW_BUF(IDS_ERROR_FEEDBACK,prefsTitle,64);
+ prefItem.proc = (void*) ConfigDlgProc;
+ prefItem.hInst = WASABI_API_LNG_HINST;
+ prefItem.where = -1;
+ SendMessageA(plugin.hwndParent, WM_WA_IPC, (WPARAM) &prefItem, IPC_ADD_PREFS_DLGW);
+ winampVersion = (char *)SendMessageA(plugin.hwndParent,WM_WA_IPC,0,IPC_GETVERSIONSTRING);
+ return GEN_INIT_SUCCESS;
+}
+
+void config(void)
+{
+ SendMessage(plugin.hwndParent,WM_WA_IPC,(WPARAM)&prefItem,IPC_OPENPREFSTOPAGE);
+}
+
+void quit(void)
+{
+ watcher.StopWatching();
+ watcher.Clear();
+
+ waServiceFactory *sf = WASABI_API_SVC->service_getServiceByGuid(languageApiGUID);
+ if (sf) sf->releaseInterface(WASABI_API_LNG);
+ WASABI_API_LNG=0;
+}
+
+int StartHandler(wchar_t* iniPath)
+{
+ settings.SetPath(iniPath);
+ if (!settings.Load())
+ {
+ if (!(settings.CreateDefault(iniPath) && settings.Save()))
+ {
+ //OutputDebugString(L"Feedback plugin - unable to read settings. Error feedback disabled.\r\n");
+ }
+ }
+ SetUnhandledExceptionFilter(FeedBackFilter);
+ //OutputDebugString(L"Error FeedBack started.\r\n");
+ return 0;
+}
+
+PEXCEPTION_POINTERS gExceptionInfo;
+
+LONG WINAPI FeedBackFilter( struct _EXCEPTION_POINTERS *pExceptionInfo )
+{
+ if (alreadyProccessing) return EXCEPTION_CONTINUE_EXECUTION;
+
+ alreadyProccessing = TRUE;
+ gExceptionInfo = pExceptionInfo;
+
+ // show user dialog
+ HWND hwnd;
+ if (WASABI_API_LNG)
+ hwnd = WASABI_API_CREATEDIALOGPARAMW(IDD_CRASHDLG, NULL, CrashDlgProc, (LPARAM)WASABI_API_ORIG_HINST);
+ else
+ #ifdef _M_IX86
+ hwnd = CreateDialogParam(plugin.hDllInstance, MAKEINTRESOURCE(IDD_CRASHDLG), NULL, CrashDlgProc, (LPARAM)plugin.hDllInstance);
+ #endif
+ #ifdef _M_X64
+ hwnd = CreateDialogParam(plugin.hDllInstance, MAKEINTRESOURCE(IDD_CRASHDLG), NULL, (DLGPROC)CrashDlgProc, (LPARAM)plugin.hDllInstance);
+ #endif
+
+ SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_SHOWWINDOW | SWP_NOSIZE | SWP_NOMOVE);
+ while (IsWindow(hwnd))
+ {
+ MSG msg;
+ if (!GetMessage(&msg, NULL, 0, 0)) break;
+ if (IsDialogMessage(hwnd, &msg)) continue;
+ TranslateMessage(&msg);
+ DispatchMessage(&msg);
+ }
+ return EXCEPTION_EXECUTE_HANDLER;
+} \ No newline at end of file
diff --git a/Src/Plugins/General/gen_crasher/main.h b/Src/Plugins/General/gen_crasher/main.h
new file mode 100644
index 00000000..ee3262f0
--- /dev/null
+++ b/Src/Plugins/General/gen_crasher/main.h
@@ -0,0 +1,24 @@
+#ifndef NULLSOFT_CRASHER_MAIN_H
+#define NULLSOFT_CRASHER_MAIN_H
+
+#include <windows.h>
+#include <dbghelp.h>
+#include "resource.h"
+#define NO_IVIDEO_DECLARE
+#include "..\winamp\wa_ipc.h"
+#include "settings.h"
+#include "../winamp/gen.h"
+
+extern Settings settings;
+extern prefsDlgRecW prefItem;
+extern char *winampVersion;
+extern "C" winampGeneralPurposePlugin plugin;
+
+extern "C" __declspec(dllexport) int StartHandler(wchar_t* iniPath);
+extern "C" LONG WINAPI FeedBackFilter( struct _EXCEPTION_POINTERS *pExceptionInfo );
+
+//typedef struct _EXCEPTION_POINTERS EXCEPTION_POINTERS, *PEXCEPTION_POINTERS;
+
+static BOOL alreadyProccessing;
+
+#endif // NULLSOFT_CRASHER_MAIN_H \ No newline at end of file
diff --git a/Src/Plugins/General/gen_crasher/minidump.h b/Src/Plugins/General/gen_crasher/minidump.h
new file mode 100644
index 00000000..139acc60
--- /dev/null
+++ b/Src/Plugins/General/gen_crasher/minidump.h
@@ -0,0 +1,29 @@
+#pragma once
+#include <dbghelp.h>
+/*
+typedef struct _MINIDUMP_EXCEPTION_INFORMATION
+{
+ DWORD ThreadId;
+ PEXCEPTION_POINTERS ExceptionPointers;
+ BOOL ClientPointers;
+} MINIDUMP_EXCEPTION_INFORMATION, *PMINIDUMP_EXCEPTION_INFORMATION;
+
+typedef enum _MINIDUMP_TYPE
+{
+ MiniDumpNormal = 0x00000000,
+ MiniDumpWithDataSegs = 0x00000001,
+ MiniDumpWithFullMemory = 0x00000002,
+ MiniDumpWithHandleData = 0x00000004,
+ MiniDumpFilterMemory = 0x00000008,
+ MiniDumpScanMemory = 0x00000010,
+ MiniDumpWithUnloaded = 0x00000020,
+ MiniDumpWithIndirectlyReferencedMemory = 0x00000040,
+ MiniDumpFilterModulePaths = 0x00000080,
+ MiniDumpWithProcessThreadData = 0x00000100,
+ MiniDumpWithPrivateReadWriteMemory = 0x00000200,
+ MiniDumpWithoutOptionalData = 0x00000400,
+ MiniDumpWithFullMemoryInfo = 0x00000800,
+ MiniDumpWithThreadInfo = 0x00001000,
+ MiniDumpWithCodeSegs = 0x00002000
+} MINIDUMP_TYPE;
+*/ \ No newline at end of file
diff --git a/Src/Plugins/General/gen_crasher/resource.h b/Src/Plugins/General/gen_crasher/resource.h
new file mode 100644
index 00000000..2c7e076d
--- /dev/null
+++ b/Src/Plugins/General/gen_crasher/resource.h
@@ -0,0 +1,96 @@
+//{{NO_DEPENDENCIES}}
+// Microsoft Visual C++ generated include file.
+// Used by gen_crasher.rc
+//
+#define IDS_ERROR_FEEDBACK 1
+#define IDS_UNKNOWN 2
+#define IDS_LOADED_OK 3
+#define IDS_UNABLE_TO_LOAD 4
+#define IDS_NOT_FOUND 5
+#define IDS_UNABLE_TO_SAVE_SETTINGS 6
+#define IDS_SAVE_ERROR 7
+#define IDS_SELECT_FOLDER_FOR_ERROR_INFO 8
+#define IDD_CRASHDLG 101
+#define IDD_CONFIG 102
+#define IDD_DLG_SMTP 103
+#define IDC_CHK_CREATEDMP 1003
+#define IDC_EDT_DMPPATH 1004
+#define IDC_EDT_DMPNAME 1004
+#define IDC_BTN_DMPPATH 1005
+#define IDC_BTN_PATH 1005
+#define IDC_CMB_DMPTYPE 1006
+#define IDC_CHK_CREATELOG 1007
+#define IDC_EDT_LOGPATH 1008
+#define IDC_EDT_LOGNAME 1008
+#define IDC_BUTTON2 1009
+#define IDC_BTN_LOGPATH 1009
+#define IDC_EDT_PATH 1009
+#define IDC_RB_USECLIENT 1010
+#define IDC_CHK_LOGSYSTEM 1011
+#define IDC_CHK_LOGFILE 1012
+#define IDC_CHK_LOGREGISTRY 1012
+#define IDC_CHK_LOGMACHINE 1013
+#define IDC_CHK_LOGSTACK 1013
+#define IDC_CHK_RESTART 1014
+#define IDC_CHK_SILENT 1015
+#define IDC_CHK_SEND 1016
+#define IDC_USESMTP 1017
+#define IDC_RB_USESMTP 1017
+#define IDC_EDT_SERVER 1018
+#define IDC_EDT_ZIPNAME 1018
+#define IDC_EDT_USER 1019
+#define IDC_EDT_PWD 1020
+#define IDC_CHK_LOGMODULE 1021
+#define IDC_EDT_PORT 1021
+#define IDC_GRP_DUMP 1022
+#define IDC_GRP_GENERAL 1023
+#define IDC_GRP_LOG 1024
+#define IDC_GRP_EMAIL 1025
+#define IDC_LBL_SERVER 1026
+#define IDC_GRP_ZIP 1026
+#define IDC_LBL_USER 1027
+#define IDC_LBL_PWD 1028
+#define IDC_LBL_DMPTYPE 1029
+#define IDC_LBL_PORT 1029
+#define IDC_LBL_DMPPATH 1030
+#define IDC_LBL_DMPNAME 1030
+#define IDC_LBL_LOGPATH 1031
+#define IDC_LBL_LOGNAME 1031
+#define IDC_LBL_OSVERSION_CAPTION 1032
+#define IDC_LBL_DLLPATH_CAPTION 1033
+#define IDC_LBL_DLLVERSION_CAPTION 1034
+#define IDC_LBL_OSVERSION 1035
+#define IDC_LBL_DLLPATH 1036
+#define IDC_LBL_DLLVERSION 1037
+#define IDC_CHK_COMPRESS 1040
+#define IDC_BTN_SMTP 1041
+#define IDC_LBL_ZIPNAME 1042
+#define IDC_LBL_PATH 1043
+#define IDC_LBL_INFO 1045
+#define IDC_LBL_STEP 1046
+#define IDC_BMP_LOGO 1047
+#define IDC_EDIT1 1048
+#define IDC_EDT_ADDRESS 1048
+#define IDC_CHK_AUTH 1049
+#define IDC_GRP_AUTH 1050
+#define IDC_LBL_ADDRESS 1051
+#define IDC_GRP_AUTH2 1052
+#define IDC_TITLELBL 1101
+#define IDC_WORKLBL 1102
+#define IDC_PROGRESS1 1103
+#define IDC_PRG_COLLECT 1103
+#define IDC_PROGRESS2 1104
+#define IDC_PROGRESS3 1105
+#define IDS_NULLSOFT_ERROR_FEEDBACK 65534
+#define IDS_STRING107 65535
+
+// Next default values for new objects
+//
+#ifdef APSTUDIO_INVOKED
+#ifndef APSTUDIO_READONLY_SYMBOLS
+#define _APS_NEXT_RESOURCE_VALUE 108
+#define _APS_NEXT_COMMAND_VALUE 40001
+#define _APS_NEXT_CONTROL_VALUE 1052
+#define _APS_NEXT_SYMED_VALUE 101
+#endif
+#endif
diff --git a/Src/Plugins/General/gen_crasher/settings.cpp b/Src/Plugins/General/gen_crasher/settings.cpp
new file mode 100644
index 00000000..d37f0ce5
--- /dev/null
+++ b/Src/Plugins/General/gen_crasher/settings.cpp
@@ -0,0 +1,251 @@
+#include ".\settings.h"
+#include <shlwapi.h>
+#include <strsafe.h>
+
+Settings::Settings(void)
+{
+ dumpPath = NULL;
+ logPath = NULL;
+ smtpServer = NULL;
+ smtpUser = NULL;
+ smtpPwd = NULL;
+ path = NULL;
+ smtpAddress = NULL;
+ updatePath = TRUE;
+ createDMP = TRUE;
+ createLOG = TRUE;
+ autoRestart = FALSE;
+ silentMode = TRUE;
+ sendData = TRUE;
+ zipData = TRUE;
+ zipPath = NULL;
+ sendByClient = TRUE;
+ sendBySMTP = FALSE;
+ smtpPort = 25;
+ smtpAuth = TRUE;
+ dumpType = 0;
+ logSystem = TRUE;
+ logRegistry = TRUE;
+ logStack = TRUE;
+ logModule = TRUE;
+}
+
+Settings::~Settings(void)
+{
+ if (dumpPath) free(dumpPath);
+ if (logPath) free(logPath);
+ if (smtpServer) free(smtpServer);
+ if (smtpUser) free(smtpUser);
+ if (smtpPwd) free(smtpPwd);
+ if (path) free(path);
+ if (smtpAddress) free(smtpAddress);
+}
+
+void Settings::SetPath(wchar_t *iniPath)
+{
+ size_t size = lstrlen(iniPath);
+ if (path) free(path);
+ path = NULL;
+ path = (wchar_t*)malloc((size + 1) * sizeof(wchar_t));
+ StringCchCopy(path, size+1, iniPath);
+ wchar_t iniFile[MAX_PATH*2] = {0};
+ size += 14 * sizeof(wchar_t);
+ CreateDirectory(iniPath, NULL);
+ StringCchPrintf(iniFile, size, L"%s\\feedback.ini", iniPath);
+ cfg.SetIniFile(iniFile);
+}
+
+const wchar_t* Settings::GetPath(void)
+{
+ return path;
+}
+
+BOOL Settings::Load(void)
+{
+ if (!cfg.IsFileExist()) return FALSE;
+ cfg.SetSection(L"General");
+ updatePath = cfg.ReadInt(L"UpdatePath", TRUE);
+ if (updatePath) return FALSE;
+ createDMP = cfg.ReadInt(L"CreateDmp", TRUE);
+ createLOG = cfg.ReadInt(L"CreateLog", TRUE);
+ autoRestart = cfg.ReadInt(L"AutoRestart", FALSE);
+ silentMode = cfg.ReadInt(L"SilentMode", TRUE);
+ sendData = cfg.ReadInt(L"SendData", TRUE);
+
+ cfg.SetSection(L"Send");
+ sendByClient = cfg.ReadInt(L"UseClient", TRUE);
+ sendBySMTP = cfg.ReadInt(L"UseSMTP", FALSE);
+ smtpPort = cfg.ReadInt(L"Port", 25);
+ smtpAuth = cfg.ReadInt(L"ReqAuth", TRUE);
+ CreateStrCopy(&smtpAddress, cfg.ReadStringW(L"Address", L"bug@winamp.com"));
+ CreateStrCopy(&smtpServer, cfg.ReadStringW(L"Server", NULL));
+ CreateStrCopy(&smtpUser, cfg.ReadStringW(L"User", NULL));
+ CreateStrCopy(&smtpPwd, cfg.ReadStringW(L"Pwd", NULL));
+
+ cfg.SetSection(L"Zip");
+ zipData = cfg.ReadInt(L"ZipData", TRUE);
+ CreateStrCopy(&zipPath, cfg.ReadStringW(L"Path", NULL));
+
+ cfg.SetSection(L"Dump");
+ dumpType = cfg.ReadInt(L"Type", 0);
+ CreateStrCopy(&dumpPath, cfg.ReadStringW(L"Path", NULL));
+
+ cfg.SetSection(L"Log");
+ logSystem = cfg.ReadInt(L"System", TRUE);
+ logRegistry = cfg.ReadInt(L"Registry", TRUE);
+ logStack = cfg.ReadInt(L"Stack", TRUE);
+ logModule = cfg.ReadInt(L"Module", TRUE);
+ CreateStrCopy(&logPath, cfg.ReadStringW(L"Path", NULL));
+ return TRUE;
+}
+
+void Settings::CreateStrCopy(wchar_t **dest, const wchar_t* source)
+{
+ if (*dest) free(*dest);
+ *dest = NULL;
+ if (source)
+ {
+ size_t len = lstrlen(source) + 1;
+ *dest = (wchar_t*) malloc(len*sizeof(wchar_t));
+ StringCchCopy(*dest, len, source);
+ }
+}
+
+BOOL Settings::Save(void)
+{
+ BOOL error = FALSE;
+ if (FALSE == cfg.SetSection(L"General")) error = TRUE;
+ if (FALSE == cfg.Write(L"UpdatePath", FALSE)) error = TRUE;
+ if (FALSE == cfg.Write(L"CreateDmp", createDMP)) error = TRUE;
+ if (FALSE == cfg.Write(L"CreateLog", createLOG)) error = TRUE;
+ if (FALSE == cfg.Write(L"AutoRestart", autoRestart)) error = TRUE;
+ if (FALSE == cfg.Write(L"SilentMode", silentMode)) error = TRUE;
+ if (FALSE == cfg.Write(L"SendData", sendData)) error = TRUE;
+ if (FALSE == cfg.SetSection(L"Send")) error = TRUE;
+ if (FALSE == cfg.Write(L"UseClient", sendByClient)) error = TRUE;
+ if (FALSE == cfg.Write(L"UseSMTP", sendBySMTP)) error = TRUE;
+ if (FALSE == cfg.Write(L"Port", smtpPort)) error = TRUE;
+ if (FALSE == cfg.Write(L"Server", smtpServer)) error = TRUE;
+ if (FALSE == cfg.Write(L"Address", smtpAddress)) error = TRUE;
+ if (FALSE == cfg.Write(L"ReqAuth", smtpAuth)) error = TRUE;
+ if (FALSE == cfg.Write(L"User", smtpUser)) error = TRUE;
+ if (FALSE == cfg.Write(L"Pwd", smtpPwd)) error = TRUE;
+ if (FALSE == cfg.SetSection(L"Zip")) error = TRUE;
+ if (FALSE == cfg.Write(L"ZipData", zipData)) error = TRUE;
+ if (FALSE == cfg.Write(L"Path", zipPath)) error = TRUE;
+ if (FALSE == cfg.SetSection(L"Dump")) error = TRUE;
+ if (FALSE == cfg.Write(L"Type", dumpType)) error = TRUE;
+ if (FALSE == cfg.Write(L"Path", dumpPath)) error = TRUE;
+ if (FALSE == cfg.SetSection(L"Log")) error = TRUE;
+ if (FALSE == cfg.Write(L"System", logSystem)) error = TRUE;
+ if (FALSE == cfg.Write(L"Registry", logRegistry)) error = TRUE;
+ if (FALSE == cfg.Write(L"Stack", logStack)) error = TRUE;
+ if (FALSE == cfg.Write(L"Module", logModule)) error = TRUE;
+ if (FALSE == cfg.Write(L"Path", logPath)) error = TRUE;
+ return !error;
+}
+
+BOOL Settings::CreateDefault(wchar_t* iniPath)
+{
+ wchar_t temp[MAX_PATH] = {0};
+ int len;
+
+ createDMP = TRUE;
+ createLOG = TRUE;
+ autoRestart = FALSE;
+ silentMode = TRUE;
+ sendData = TRUE;
+// zip
+ PathCombine(temp, iniPath, L"report.zip");
+ len = (int)wcslen(temp) + 1;
+ zipData = TRUE;
+ zipPath = (wchar_t*) malloc(len*2);
+ StringCchCopy(zipPath, len, temp);
+// send
+ sendByClient = TRUE;
+ sendBySMTP = FALSE;
+ smtpPort = 25;
+ smtpAddress = (wchar_t*) malloc(32*2);
+ StringCchCopy(smtpAddress, 32, L"bug@winamp.com");
+ smtpAuth = TRUE;
+ smtpServer = NULL;
+ smtpUser = NULL;
+ smtpPwd = NULL;
+// dump
+ PathCombine(temp, iniPath, L"_crash.dmp");
+ len = (int)wcslen(temp) + 1;
+ dumpType = NULL;
+ dumpPath = (wchar_t*) malloc(len*2);
+ StringCchCopy(dumpPath, len, temp);
+// log
+ logSystem = TRUE;
+ logRegistry = TRUE;
+ logStack = TRUE;
+ logModule = TRUE;
+ PathCombine(temp, iniPath, L"_crash.log");
+ len = (int)wcslen(temp) + 1;
+ logPath = (wchar_t*) malloc(len*2);
+ StringCchCopy(logPath, len, temp);
+ return TRUE;
+}
+
+BOOL Settings::IsOk(void)
+{
+ return (logPath != NULL && dumpPath != NULL);
+}
+
+void Settings::ClearTempData(void)
+{
+ cfg.Write(L"Temp", L"TS", L"");
+ cfg.Write(L"Temp", L"LOG", L"0");
+ cfg.Write(L"Temp", L"DMP", L"0");
+}
+
+void Settings::WriteErrorTS(const wchar_t *time)
+{
+ cfg.Write(L"Temp", L"TS", time);
+}
+
+void Settings::WriteLogCollectResult(BOOL result)
+{
+ cfg.Write(L"Temp", L"LOG", result);
+}
+
+void Settings::WriteDmpCollectResult(BOOL result)
+{
+ cfg.Write(L"Temp", L"DMP", result);
+}
+
+void Settings::WriteWinamp(const wchar_t *winamp)
+{
+ cfg.Write(L"Temp", L"WA", winamp);
+}
+
+const wchar_t* Settings::ReadErrorTS(void)
+{
+ return cfg.ReadStringW(L"Temp", L"TS", L"");
+}
+
+BOOL Settings::ReadLogCollectResult(void)
+{
+ return cfg.ReadInt(L"Temp", L"LOG", 0);
+}
+BOOL Settings::ReadDmpCollectResult(void)
+{
+ return cfg.ReadInt(L"Temp", L"DMP", 0);
+}
+
+const wchar_t* Settings::ReadWinamp(void)
+{
+ return cfg.ReadStringW(L"Temp", L"WA", L"");
+}
+
+void Settings::WriteBody(const wchar_t *body)
+{
+ cfg.Write(L"Temp", L"Body", body);
+}
+
+const wchar_t* Settings::ReadBody(void)
+{
+ return cfg.ReadStringW(L"Temp", L"Body", L"");
+} \ No newline at end of file
diff --git a/Src/Plugins/General/gen_crasher/settings.h b/Src/Plugins/General/gen_crasher/settings.h
new file mode 100644
index 00000000..e90932fb
--- /dev/null
+++ b/Src/Plugins/General/gen_crasher/settings.h
@@ -0,0 +1,67 @@
+#pragma once
+
+#include "config.h"
+
+class Settings
+{
+public:
+ Settings(void);
+ ~Settings(void);
+
+public:
+ void SetPath(wchar_t *iniPath);
+ BOOL Load(void);
+ BOOL Save(void);
+ BOOL CreateDefault(wchar_t* iniPath);
+ BOOL IsOk(void);
+ const wchar_t* GetPath(void);
+
+protected:
+ void CreateStrCopy(wchar_t **dest, const wchar_t* source);
+private:
+ ConfigW cfg;
+ wchar_t* path;
+
+public:
+// general
+ BOOL updatePath;
+ BOOL createDMP;
+ BOOL createLOG;
+ BOOL autoRestart;
+ BOOL silentMode;
+ BOOL sendData;
+//zip
+ BOOL zipData;
+ wchar_t* zipPath;
+// send
+ BOOL sendByClient;
+ BOOL sendBySMTP;
+ int smtpPort;
+ wchar_t *smtpServer;
+ wchar_t *smtpAddress;
+ BOOL smtpAuth;
+ wchar_t *smtpUser;
+ wchar_t *smtpPwd;
+// dump
+ int dumpType;
+ wchar_t *dumpPath;
+// log
+ BOOL logSystem;
+ BOOL logRegistry;
+ BOOL logStack;
+ BOOL logModule;
+ wchar_t *logPath;
+// tmp
+ void ClearTempData(void);
+ void WriteErrorTS(const wchar_t *time);
+ void WriteLogCollectResult(BOOL result);
+ void WriteDmpCollectResult(BOOL result);
+ void WriteWinamp(const wchar_t *winamp);
+ void WriteBody(const wchar_t *body);
+
+ const wchar_t* ReadErrorTS(void);
+ BOOL ReadLogCollectResult(void);
+ BOOL ReadDmpCollectResult(void);
+ const wchar_t* ReadWinamp(void);
+ const wchar_t* ReadBody(void);
+}; \ No newline at end of file
diff --git a/Src/Plugins/General/gen_crasher/smtpDlg.cpp b/Src/Plugins/General/gen_crasher/smtpDlg.cpp
new file mode 100644
index 00000000..ed7a34dd
--- /dev/null
+++ b/Src/Plugins/General/gen_crasher/smtpDlg.cpp
@@ -0,0 +1,127 @@
+#include ".\smtpdlg.h"
+#include ".\resource.h"
+#include ".\settings.h"
+
+#include <strsafe.h>
+
+extern Settings settings;
+
+void UpdateAuth(HWND hwndDlg, BOOL enabled)
+{
+ EnableWindow(GetDlgItem(hwndDlg, IDC_LBL_USER), enabled);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_EDT_USER), enabled);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_LBL_PWD), enabled);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_EDT_PWD), enabled);
+}
+
+BOOL CALLBACK smtpDlgProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+ switch(uMsg)
+ {
+ case WM_INITDIALOG:
+ {
+ wchar_t num[16] = {0};
+ CenterDialog(hwndDlg);
+ SetWindowText(GetDlgItem(hwndDlg, IDC_EDT_SERVER), settings.smtpServer);
+ SetWindowText(GetDlgItem(hwndDlg, IDC_EDT_USER), settings.smtpUser);
+ SetWindowText(GetDlgItem(hwndDlg, IDC_EDT_PWD), settings.smtpPwd);
+ SetWindowText(GetDlgItem(hwndDlg, IDC_EDT_PORT), _itow(settings.smtpPort, num, 10));
+ SetWindowText(GetDlgItem(hwndDlg, IDC_EDT_ADDRESS), settings.smtpAddress);
+ CheckDlgButton(hwndDlg, IDC_CHK_AUTH, settings.smtpAuth);
+ UpdateAuth(hwndDlg, settings.smtpAuth);
+ break;
+ }
+ case WM_DESTROY:
+ {
+ wchar_t buf[1024] = {0};
+ int len;
+ if (settings.smtpServer) free(settings.smtpServer);
+ settings.smtpServer = NULL;
+ len = GetWindowText(GetDlgItem(hwndDlg, IDC_EDT_SERVER), buf, 1024);
+ if (len)
+ {
+ settings.smtpServer = (wchar_t*)malloc((len + 1)*2);
+ StringCchCopy(settings.smtpServer, len+1, buf);
+ }
+
+ len = GetWindowText(GetDlgItem(hwndDlg, IDC_EDT_PORT), buf, 1024);
+ if (len) settings.smtpPort = _wtoi(buf);
+
+ if (settings.smtpUser) free(settings.smtpUser);
+ settings.smtpUser = NULL;
+ len = GetWindowText(GetDlgItem(hwndDlg, IDC_EDT_USER), buf, 1024);
+ if (len)
+ {
+ settings.smtpUser = (wchar_t*)malloc((len + 1)*2);
+ StringCchCopy(settings.smtpUser, len+1, buf);
+ }
+
+ if (settings.smtpPwd) free(settings.smtpPwd);
+ settings.smtpPwd = NULL;
+ len = GetWindowText(GetDlgItem(hwndDlg, IDC_EDT_PWD), buf, 1024);
+ if (len)
+ {
+ settings.smtpPwd = (wchar_t*)malloc((len + 1)*2);
+ StringCchCopy(settings.smtpPwd, len+1, buf);
+ }
+
+ if (settings.smtpAddress) free(settings.smtpAddress);
+ settings.smtpAddress = NULL;
+ len = GetWindowText(GetDlgItem(hwndDlg, IDC_EDT_ADDRESS), buf, 1024);
+ if (len)
+ {
+ settings.smtpAddress = (wchar_t*)malloc((len + 1)*2);
+ StringCchCopy(settings.smtpAddress, len+1, buf);
+ }
+ settings.smtpAuth = (SendMessage(GetDlgItem(hwndDlg, IDC_CHK_AUTH), BM_GETCHECK, 0,0) == BST_CHECKED);
+ settings.Save();
+ break;
+ }
+ case WM_COMMAND:
+ switch(LOWORD(wParam))
+ {
+ case IDC_CHK_AUTH:
+ UpdateAuth(hwndDlg, (SendMessage((HWND) lParam, BM_GETCHECK, 0,0) == BST_CHECKED));
+ break;
+ case IDCANCEL:
+ EndDialog(hwndDlg, 0);
+ break;
+ }
+ break;
+
+ }
+ return FALSE;
+}
+
+void CenterDialog(HWND hwndDlg)
+{
+ HWND hwndOwner;
+ RECT rc, rcDlg, rcOwner;
+ if ((hwndOwner = GetParent(hwndDlg)) == NULL)
+ {
+ hwndOwner = GetDesktopWindow();
+ }
+
+ GetWindowRect(hwndOwner, &rcOwner);
+ GetWindowRect(hwndDlg, &rcDlg);
+ CopyRect(&rc, &rcOwner);
+
+ // Offset the owner and dialog box rectangles so that
+ // right and bottom values represent the width and
+ // height, and then offset the owner again to discard
+ // space taken up by the dialog box.
+
+ OffsetRect(&rcDlg, -rcDlg.left, -rcDlg.top);
+ OffsetRect(&rc, -rc.left, -rc.top);
+ OffsetRect(&rc, -rcDlg.right, -rcDlg.bottom);
+
+ // The new position is the sum of half the remaining
+ // space and the owner's original position.
+
+ SetWindowPos(hwndDlg,
+ HWND_TOP,
+ rcOwner.left + (rc.right / 2),
+ rcOwner.top + (rc.bottom / 2),
+ 0, 0, // ignores size arguments
+ SWP_NOSIZE);
+} \ No newline at end of file
diff --git a/Src/Plugins/General/gen_crasher/smtpDlg.h b/Src/Plugins/General/gen_crasher/smtpDlg.h
new file mode 100644
index 00000000..3ddac466
--- /dev/null
+++ b/Src/Plugins/General/gen_crasher/smtpDlg.h
@@ -0,0 +1,5 @@
+#pragma once
+#include <windows.h>
+
+void CenterDialog(HWND hwndDlg);
+BOOL CALLBACK smtpDlgProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam); \ No newline at end of file
diff --git a/Src/Plugins/General/gen_crasher/version.rc2 b/Src/Plugins/General/gen_crasher/version.rc2
new file mode 100644
index 00000000..e06748f6
--- /dev/null
+++ b/Src/Plugins/General/gen_crasher/version.rc2
@@ -0,0 +1,39 @@
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Version
+//
+#include "..\..\..\Winamp/buildType.h"
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION 1,16,0,0
+ PRODUCTVERSION WINAMP_PRODUCTVER
+ FILEFLAGSMASK 0x17L
+#ifdef _DEBUG
+ FILEFLAGS 0x1L
+#else
+ FILEFLAGS 0x0L
+#endif
+ FILEOS 0x4L
+ FILETYPE 0x2L
+ FILESUBTYPE 0x0L
+BEGIN
+ BLOCK "StringFileInfo"
+ BEGIN
+ BLOCK "040904b0"
+ BEGIN
+ VALUE "CompanyName", "Winamp SA"
+ VALUE "FileDescription", "Winamp General Purpose Plug-in"
+ VALUE "FileVersion", "1,16,0,0"
+ VALUE "InternalName", "Nullsoft Winamp Error Feedback Plug-in"
+ VALUE "LegalCopyright", "Copyright © 2005-2023 Winamp SA"
+ VALUE "LegalTrademarks", "Nullsoft and Winamp are trademarks of Winamp SA"
+ VALUE "OriginalFilename", "gen_crasher.dll"
+ VALUE "ProductName", "Winamp"
+ VALUE "ProductVersion", STR_WINAMP_PRODUCTVER
+ END
+ END
+ BLOCK "VarFileInfo"
+ BEGIN
+ VALUE "Translation", 0x409, 1200
+ END
+END