/*****************************************************************************
 *
 *	map.h - Main private header file
 *
 *****************************************************************************/

/*****************************************************************************
 *
 *	Coding conventions:
 *
 *	+ Follow standard shell coding conventions.
 *
 *	+ Standard K&R brace placement and indentation style.
 *
 *	+ Indent by 4 spaces.
 *
 *	+ Fully-brace all dependent clauses.  Never write "if (c) foo();"
 *
 *	+ Do not return in the middle of a function.  If forced,
 *	  use a "goto exit".  This way, you can stick entry/exit stuff
 *	  later without getting caught out.  (I learned this rule the
 *	  hard way.)
 *
 *	+ Declare variables with narrowest possible scope.
 *
 *	+ Always test for success, not failure!  The compiler will
 *	  thank you.
 *
 *****************************************************************************/

/*****************************************************************************
 *
 *	NOTE!  This code was written for readability, not efficiency.
 *
 *	I'm trusting the compiler to do optimizations like these:
 *
 *	"Parameter alias":
 *
 *	    Function(LPFOO pfoo)
 *	    {
 *		LPBAR pbar = (LPBAR)pfoo;
 *		... use pbar and never mention pfoo again ...
 *	    }
 *
 *	    --> becomes
 *
 *	    Function(LPFOO pfoo)
 *	    {
 *		#define pbar ((LPBAR)pfoo)
 *		... use pbar and never mention pfoo again ...
 *		#undef pbar
 *	    }
 *
 *	"Speculative Execution":
 *
 *	    Function(PFOO pfoo)
 *	    {
 *		BOOL fRc;
 *		if (... condition 1 ...) {
 *		    ... complicated stuff ...
 *		    *pfoo = result;
 *		    fRc = 1;
 *		} else {		// condition 1 failed
 *		    *pfoo = 0;
 *		    fRc = 0;
 *		}
 *		return fRc;
 *	    }
 *
 *	    --> becomes
 *
 *	    Function(PFOO pfoo)
 *	    {
 *		BOOL fRc = 0;
 *		*pfoo = 0;
 *		if (... condition 1 ...) {
 *		    ... complicated stuff ...
 *		    *pfoo = result;
 *		    fRc = 1;
 *		}
 *		return fRc;
 *	    }
 *
 *	"Single Exit":
 *
 *	    Function(...)
 *	    {
 *		BOOL fRc;
 *		if (... condition 1 ...) {
 *		    ...
 *		    if (... condition 2 ...) {
 *			...
 *			fRc = 1;
 *		    } else {		// condition 2 failed
 *			... clean up ...
 *			fRc = 0;
 *		    }
 *		} else {		// condition 1 failed
 *		    ... clean up ...
 *		    fRc = 0;
 *		}
 *		return fRc;
 *	    }
 *
 *	    --> becomes
 *
 *	    Function(...)
 *	    {
 *		if (... condition 1 ...) {
 *		    ...
 *		    if (... condition 2 ...) {
 *			...
 *			return 1;
 *		    } else {		// condition 2 failed
 *			... clean up ...
 *			return 0;
 *		    }
 *		} else {		// condition 1 failed
 *		    ... clean up ...
 *		    return 0;
 *		}
 *		NOTREACHED;
 *	    }
 *
 *
 *
 *****************************************************************************/

#define STRICT
#undef  WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#define NOIME
#define NOSERVICE
#undef WINVER
#undef _WIN32_WINDOWS
#define WINVER 0x0400           /* Windows 4.0 compatible */
#define _WIN32_WINDOWS 0x0400   /* Windows 4.0 compatible */
#include <windows.h>

#ifdef	RC_INVOKED		/* Define some tags to speed up rc.exe */
#define __RPCNDR_H__		/* Don't need RPC network data representation */
#define __RPC_H__		/* Don't need RPC */
#include <oleidl.h>		/* Get the DROPEFFECT stuff */
#define _OLE2_H_		/* But none of the rest */
#define _WINDEF_
#define _WINBASE_
#define _WINGDI_
#define NONLS
#define _WINCON_
#define _WINREG_
#define _WINNETWK_
#define _INC_COMMCTRL
#define _INC_SHELLAPI
#else
#include <windowsx.h>
#include <regstr.h>
#endif

#include <shlobj.h>
#include <shellapi.h>

#ifdef DBG                          /* NT build process uses DBG */
#define DEBUG
#endif

/*****************************************************************************
 *
 *      Int64 goo.
 *
 *****************************************************************************/

#define SetWindowPointer(hwnd, i, p) SetWindowLongPtr(hwnd, i, (LRESULT)(p))
#define GetWindowPointer(hwnd, i)    (void *)GetWindowLongPtr(hwnd, i)

/*****************************************************************************
 *
 *	Stuff
 *
 *****************************************************************************/

#define IToClass(T, f, p)   CONTAINING_RECORD(p, T, f)
#define _IOffset(T, f)      FIELD_OFFSET(T, f)

/*****************************************************************************
 *
 *	Resource identifiers
 *
 *****************************************************************************/

/*****************************************************************************
 *
 *	Dialogs
 *
 *****************************************************************************/

#define IDC_STATIC		-1

#define IDD_MAIN		1

#define IDC_FROM                16
#define IDC_TO                  17

/*****************************************************************************
 *
 *	Strings
 *
 *****************************************************************************/

#define IDS_KEYFIRST            32
#define IDS_CAPSLOCK            32
#define IDS_LCTRL               33
#define IDS_RCTRL               34
#define IDS_LALT                35
#define IDS_RALT                36
#define IDS_LSHIFT              37
#define IDS_RSHIFT              38
#define IDS_LWIN                39
#define IDS_RWIN                40
#define IDS_APPS                41
#define IDS_KEYLAST             41
#define IDS_NUMKEYS             (IDS_KEYLAST - IDS_KEYFIRST + 1)

#ifndef	RC_INVOKED

/*****************************************************************************
 *
 *	Stuff I'm tired of typing over and over.
 *
 *****************************************************************************/

typedef LPITEMIDLIST PIDL, *PPIDL;
typedef LPCITEMIDLIST PCIDL;
typedef LPSHELLFOLDER PSF;
typedef LPVOID PV;
typedef LPVOID *PPV;
typedef LPCVOID PCV;
typedef REFIID RIID;
typedef LPUNKNOWN PUNK;

/*****************************************************************************
 *
 *	Baggage - Stuff I carry everywhere
 *
 *****************************************************************************/

#define INTERNAL NTAPI	/* Called only within a translation unit */
#define EXTERNAL NTAPI	/* Called from other translation units */
#define INLINE static __inline

#define BEGIN_CONST_DATA data_seg(".text", "CODE")
#define END_CONST_DATA data_seg(".data", "DATA")

#define OBJAT(T, v) (*(T *)(v))		/* Pointer punning */
#define PUN(T, v) OBJAT(T, &(v))	/* General-purpose type-punning */

/*
 * Convert a count of TCHAR's to a count of bytes.
 */
#define cbCtch(ctch) ((ctch) * sizeof(TCHAR))

/*
 * Convert an object (X) to a count of bytes (cb).
 */
#define cbX(X) sizeof(X)

/*
 * Convert an array name (A) to a generic count (c).
 */
#define cA(a) (cbX(a)/cbX(a[0]))

/*
 * Convert an array name (A) to a pointer to its Max.
 * (I.e., one past the last element.)
 */
#define pvMaxA(a) (&a[cA(a)])

#define pvSubPvCb(pv, cb) ((PV)((PBYTE)pv - (cb)))
#define pvAddPvCb(pv, cb) ((PV)((PBYTE)pv + (cb)))
#define cbSubPvPv(p1, p2) ((PBYTE)(p1) - (PBYTE)(p2))

/*
 * Round cb up to the nearest multiple of cbAlign.  cbAlign must be
 * a power of 2 whose evaluation entails no side-effects.
 */
#define ROUNDUP(cb, cbAlign) ((((cb) + (cbAlign) - 1) / (cbAlign)) * (cbAlign))

/*
 * lfNeVV
 *
 * Given two values, return zero if they are equal and nonzero if they
 * are different.  This is the same as (v1) != (v2), except that the
 * return value on unequal is a random nonzero value instead of 1.
 * (lf = logical flag)
 *
 * lfNePvPv
 *
 * The same as lfNeVV, but for pointers.
 *
 * lfPv
 *
 * Nonzero if pv is not null.
 *
 */
#define lfNeVV(v1, v2) ((v1) - (v2))
#define lfNePvPv(v1, v2) lfNeVV((DWORD)(PV)(v1), (DWORD)(PV)(v2))
#define lfPv(pv) ((BOOL)(PV)(pv))

/*
 * land -- Logical and.  Evaluate the first.  If the first is zero,
 * then return zero.  Otherwise, return the second.
 */

#define fLandFF(f1, f2) ((f1) ? (f2) : 0)

/*
 * lor -- Logical or.  Evaluate the first.  If the first is nonzero,
 * return it.  Otherwise, return the second.
 *
 * Unfortunately, due to the stupidity of the C language, this can
 * be implemented only with a GNU extension.  In the non-GNU case,
 * we return 1 if the first is nonzero.
 */

#if defined(__GNUC__)
#define fLorFF(f1, f2) ({ typeof (f1) _f = f1; if (!_f) _f = f2; _f; })
#else
#define fLorFF(f1, f2) ((f1) ? 1 : (f2))
#endif

/*
 * limp - logical implication.  True unless the first is nonzero and
 * the second is zero.
 */
#define fLimpFF(f1, f2) (!(f1) || (f2))

/*
 * leqv - logical equivalence.  True if both are zero or both are nonzero.
 */
#define fLeqvFF(f1, f2) (!(f1) == !(f2))

/*
 * InOrder - checks that i1 <= i2 < i3.
 */
#define fInOrder(i1, i2, i3) ((unsigned)((i2)-(i1)) < (unsigned)((i3)-(i1)))

/*
 * CopyPvPvCb - Copy some memory around
 * MovePvPvCb - Move some memory around
 */
#define CopyPvPvCb RtlCopyMemory
#define MovePvPvCb RtlMoveMemory

/*
 * memeq - Reverse of memcmp
 */
#define memeq !memcmp

/*
 * fPvPfnCmpPv - Compare two objects for equality using the comparison
 *		 function and the desired outcome.  E.g.,
 *
 *			fPvPfnCmpPv(psz1, lstrcmpi, >, psz2)
 *
 *		 returns nonzero if psz1 is greater than psz2 according
 *		 to lstrcmpi.
 */

#define fPvPfnCmpPv(p1, pfn, cmp, p2) (pfn(p1, p2) cmp 0)

/*
 * lstreq   - nonzero if two strings (according to lstrcmp) are equal
 * lstrne   - nonzero if two strings (according to lstrcmp) are different
 *
 * lstrieq   - nonzero if two strings (according to lstrcmpi) are equal
 * lstrine   - nonzero if two strings (according to lstrcmpi) are different
 *
 * lstrieqA  - nonzero if two strings (according to lstrcmpiA) are equal
 * lstrineA  - nonzero if two strings (according to lstrcmpiA) are different
 */

#define lstreq   !lstrcmp
#define lstrne   lstrcmp

#define lstrieq  !lstrcmpi
#define lstrine  lstrcmpi

#define lstrieqA !lstrcmpiA
#define lstrineA lstrcmpiA

/*****************************************************************************
 *
 *	Wrappers and other quickies
 *
 *****************************************************************************/

#define pvExchangePpvPv(ppv, pv) \
	InterlockedExchangePointer(ppv, pv)

#define hresUs(us) MAKE_HRESULT(SEVERITY_SUCCESS, 0, (USHORT)(us))
#define hresLe(le) MAKE_HRESULT(SEVERITY_ERROR, FACILITY_WIN32, (USHORT)(le))

#define ILIsSimple(pidl) (ILIsEmpty(_ILNext(pidl)))

/*****************************************************************************
 *
 *	Static globals:  Initialized at PROCESS_ATTACH and never modified.
 *
 *****************************************************************************/

HINSTANCE g_hinst;		/* My instance handle */
DEFINE_GUID(CLSID_KeyRemap, 0x176AA2C0, 0x9E15, 0x11cf,
		            0xbf,0xc7,0x44,0x45,0x53,0x54,0,0);

/*****************************************************************************
 *
 *	Dynamic Globals.  There should be as few of these as possible.
 *
 *	All access to dynamic globals must be thread-safe.
 *
 *****************************************************************************/

ULONG g_cRef;			/* Global reference count */

/*****************************************************************************
 *
 *	mapcf.c - Class Factory
 *
 *****************************************************************************/

STDMETHODIMP CMapFactory_New(RIID riid, PPV ppvObj);

/*****************************************************************************
 *
 *	mappsx.c - IPropSheetExt, IShellExtInit
 *
 *****************************************************************************/

STDMETHODIMP CMapPsx_New(RIID riid, PPV ppvObj);

/*****************************************************************************
 *
 *	mapps.c - Property sheet
 *
 *****************************************************************************/

INT_PTR CALLBACK MapPs_DlgProc(HWND hdlg, UINT wm, WPARAM wParam, LPARAM lParam);

/*****************************************************************************
 *
 *	Common object managers.
 *
 *****************************************************************************/

typedef struct PREVTBL0 {		/* Simple (non-OLE) object */
    void (NTAPI *FinalizeProc)(PV pv);	/* Finalization procedure */
} PREVTBL0, *PPREVTBL0;

typedef struct PREVTBL {		/* Primary interface */
    REFIID riid;			/* Type of this object */
    void (NTAPI *FinalizeProc)(PV pv);	/* Finalization procedure */
} PREVTBL, *PPREVTBL;

typedef struct PREVTBL2 {		/* Secondary interface */
    ULONG lib;				/* offset from start of object */
} PREVTBL2, *PPREVTBL2;

#ifdef	DEBUG

#define Simple_Interface(C) 		Primary_Interface(C, IUnknown); \
					Default_QueryInterface(C) \
					Default_AddRef(C) \
					Default_Release(C)
#define Simple_Vtbl(C)	    		Primary_Vtbl(C)
#define Simple_Interface_Begin(C)	Primary_Interface_Begin(C, IUnknown)
#define Simple_Interface_End(C)	    	Primary_Interface_End(C, IUnknown)

#else

#define Simple_Interface(C) 		Primary_Interface(C, IUnknown)
#define Simple_Vtbl(C)	    		Primary_Vtbl(C)
#define Simple_Interface_Begin(C)	\
	struct S_##C##Vtbl c_####C##VI = { {		\
	    &IID_##IUnknown,				\
	    C##_Finalize,				\
	}, {						\
	    Common##_QueryInterface,			\
	    Common##_AddRef,				\
	    Common##_Release,				\

#define Simple_Interface_End(C)	    	Primary_Interface_End(C, IUnknown)

#endif

#define Primary_Interface(C, I)				\
	extern struct S_##C##Vtbl {			\
	    PREVTBL prevtbl;				\
	    I##Vtbl vtbl;				\
	} c_##C##VI					\

#define Primary_Vtbl(C) &c_##C##VI.vtbl

#define Primary_Interface_Begin(C, I)			\
	struct S_##C##Vtbl c_####C##VI = { {		\
	    &IID_##I,					\
	    C##_Finalize,				\
	}, {						\
	    C##_QueryInterface,				\
	    C##_AddRef,					\
	    C##_Release,				\

#define Primary_Interface_End(C, I)			\
	} };						\

#define Secondary_Interface(C, I)			\
	extern struct S_##I##_##C##Vtbl {		\
	    PREVTBL2 prevtbl;	 			\
	    I##Vtbl vtbl;	 			\
	} c_##I##_##C##VI				\

#define Secondary_Vtbl(C, I) &c_##I##_##C##VI.vtbl

#define Secondary_Interface_Begin(C, I, nm)		\
	struct S_##I##_##C##Vtbl c_##I##_##C##VI = { {	\
	    _IOffset(C, nm),				\
	}, {						\
	    Forward_QueryInterface,			\
	    Forward_AddRef,				\
	    Forward_Release,				\

#define Secondary_Interface_End(C, I, nm)		\
	} };						\

STDMETHODIMP Common_QueryInterface(PV, REFIID, PPV);
STDMETHODIMP_(ULONG) _Common_AddRef(PV pv);
STDMETHODIMP_(ULONG) _Common_Release(PV pv);

/*
 * In DEBUG, go through the vtbl for additional squirties.
 */
#ifdef	DEBUG
#define Common_AddRef(punk) \
		((IUnknown *)(punk))->lpVtbl->AddRef((IUnknown *)(punk))
#define Common_Release(punk) \
		((IUnknown *)(punk))->lpVtbl->Release((IUnknown *)(punk))
#else
#define Common_AddRef _Common_AddRef
#define Common_Release _Common_Release
#endif

void EXTERNAL Common_Finalize(PV);

STDMETHODIMP _Common_New(ULONG cb, PV vtbl, PPV ppvObj);
#define Common_NewCb(cb, C, ppvObj) _Common_New(cb, Primary_Vtbl(C), ppvObj)
#define Common_New(C, ppvObj) Common_NewCb(cbX(C), C, ppvObj)

STDMETHODIMP Forward_QueryInterface(PV pv, REFIID riid, PPV ppvObj);
STDMETHODIMP_(ULONG) Forward_AddRef(PV pv);
STDMETHODIMP_(ULONG) Forward_Release(PV pv);

/*****************************************************************************
 *
 *	Common_CopyAddRef
 *
 *	Copy a pointer and increment its reference count.
 *
 *	Cannot be a macro because Common_AddRef evaluates its argument
 *	twice.
 *
 *****************************************************************************/

INLINE void Common_CopyAddRef(PV pvDst, PV pvSrc)
{
    PPV ppvDst = pvDst;
    *ppvDst = pvSrc;
    Common_AddRef(pvSrc);
}

/*****************************************************************************
 *
 *	Invoking OLE methods.
 *
 *	Invoke_Release is called with a pointer to the object, not with
 *	the object itself.  It zeros out the variable on the release.
 *
 *****************************************************************************/

void EXTERNAL Invoke_AddRef(PV pv);
void EXTERNAL Invoke_Release(PV pv);

/*****************************************************************************
 *
 *	assert.c - Assertion stuff
 *
 *****************************************************************************/

#define AssertNow(c) switch(0) case 0: case c:
#define CAssertNowPP(c,l) INLINE void Assert##l(void) { AssertNow(c); }
#define CAssertNowP(c,l) CAssertNowPP(c,l)
#define CAssertNow(c) CAssertNowP(c,__LINE__)

typedef enum {
    sqflAlways		= 0x00000000,		/* Unconditional */
    sqflDll		= 0x00000001,		/* Dll bookkeeping */
    sqflFactory		= 0x00000002,		/* IClassFactory */
    sqflPsx		= 0x00000004,		/* IPropSheetExt */
    sqflPs		= 0x00000008,		/* Property sheet */
    sqflCommon		= 0x00000000,		/* common.c */
    sqflError		= 0x80000000,		/* Errors */
} SQFL;						/* squiffle */

void EXTERNAL SquirtSqflPtszV(SQFL sqfl, LPCTSTR ptsz, ...);
int EXTERNAL AssertPtszPtszLn(LPCTSTR ptszExpr, LPCTSTR ptszFile, int iLine);

#ifndef	DEBUG
#define SquirtSqflPtszV sizeof
#endif

/*****************************************************************************
 *
 *	Procedure enter/exit tracking.
 *
 *	Start a procedure with
 *
 *	EnterProc(ProcedureName, (_ "format", arg, arg, arg, ...));
 *
 *	The format string is documented in EmitPal.
 *
 *	End a procedure with one of the following:
 *
 *	    ExitProc();
 *
 *		Procedure returns no value.
 *
 *	    ExitProcX();
 *
 *		Procedure returns an arbitrary DWORD.
 *
 *	    ExitOleProc();
 *
 *		Procedure returns an HRESULT (named "hres").
 *
 *	    ExitOleProcPpv(ppvOut);
 *
 *		Procedure returns an HRESULT (named "hres") and, on success,
 *		puts a new object in ppvOut.
 *
 *****************************************************************************/

#define cpvArgMax	10	/* Max of 10 args per procedure */

typedef struct ARGLIST {
    LPCSTR pszProc;
    LPCSTR pszFormat;
    PV rgpv[cpvArgMax];
} ARGLIST, *PARGLIST;

void EXTERNAL ArgsPalPszV(PARGLIST pal, LPCSTR psz, ...);
void EXTERNAL EnterSqflPszPal(SQFL sqfl, LPCTSTR psz, PARGLIST pal);
void EXTERNAL ExitSqflPalHresPpv(SQFL, PARGLIST, HRESULT, PPV);

#ifdef	DEBUG

SQFL sqflCur;

#define AssertFPtsz(c, ptsz) \
	((c) ? 0 : AssertPtszPtszLn(ptsz, TEXT(__FILE__), __LINE__))
#define ValidateF(c) \
	((c) ? 0 : AssertPtszPtszLn(TEXT(#c), TEXT(__FILE__), __LINE__))
#define D(x)		x

#define SetupEnterProc(nm)				\
	static CHAR s_szProc[] = #nm;			\
	ARGLIST _al[1]					\

#define _ _al,

#define ppvBool	((PPV)1)
#define ppvVoid	((PPV)2)

#define DoEnterProc(v)					\
	ArgsPalPszV v;					\
	EnterSqflPszPal(sqfl, s_szProc, _al)		\

#define EnterProc(nm, v)				\
	SetupEnterProc(nm);				\
	DoEnterProc(v)					\

#define ExitOleProcPpv(ppv)				\
	ExitSqflPalHresPpv(sqfl, _al, hres, (PPV)(ppv))	\

#define ExitOleProc()					\
	ExitOleProcPpv(0)				\

#define ExitProc()					\
	ExitSqflPalHresPpv(sqfl, _al, 0, ppvVoid)	\

#define ExitProcX(x)					\
	ExitSqflPalHresPpv(sqfl, _al, (HRESULT)(x), ppvBool) \

#else

#define AssertFPtsz(c, ptsz)
#define ValidateF(c)	(c)
#define D(x)

#define SetupEnterProc(nm)
#define DoEnterProc(v)
#define EnterProc(nm, v)
#define ExitOleProcPpv(ppv)
#define ExitOleProc()
#define ExitProc()

#endif

#define AssertF(c)	AssertFPtsz(c, TEXT(#c))

/*****************************************************************************
 *
 *	Macros that forward to the common handlers after squirting.
 *	Use these only in DEBUG.
 *
 *	It is assumed that sqfl has been #define'd to the appropriate sqfl.
 *
 *****************************************************************************/

#ifdef  DEBUG

#define Default_QueryInterface(Class)				\
STDMETHODIMP							\
Class##_QueryInterface(PV pv, RIID riid, PPV ppvObj)		\
{								\
    SquirtSqflPtszV(sqfl, TEXT(#Class) TEXT("_QueryInterface()")); \
    return Common_QueryInterface(pv, riid, ppvObj);		\
}								\

#define Default_AddRef(Class)					\
STDMETHODIMP_(ULONG)						\
Class##_AddRef(PV pv)						\
{								\
    ULONG ulRc = _Common_AddRef(pv);				\
    SquirtSqflPtszV(sqfl, TEXT(#Class)				\
			TEXT("_AddRef(%08x) -> %d"), pv, ulRc); \
    return ulRc;						\
}								\

#define Default_Release(Class)					\
STDMETHODIMP_(ULONG)						\
Class##_Release(PV pv)						\
{								\
    ULONG ulRc = _Common_Release(pv);				\
    SquirtSqflPtszV(sqfl, TEXT(#Class)				\
		       TEXT("_Release(%08x) -> %d"), pv, ulRc); \
    return ulRc;						\
}								\

#endif

/*****************************************************************************
 *
 *	mem.c
 *
 *	Be extremely careful with FreePv, because it doesn't work if
 *	the pointer is null.
 *
 *****************************************************************************/

STDMETHODIMP EXTERNAL ReallocCbPpv(UINT cb, PV ppvObj);
STDMETHODIMP EXTERNAL AllocCbPpv(UINT cb, PV ppvObj);

#define FreePpv(ppv) ReallocCbPpv(0, ppv)
#define FreePv(pv) LocalFree((HLOCAL)(pv))

#endif /* !RC_INVOKED */
