399 lines
9.9 KiB
C++
399 lines
9.9 KiB
C++
#include <windows.h>
|
|
#include "util.h"
|
|
|
|
#ifndef ASSERT
|
|
#if defined(_DEBUG) || defined(DEBUG)
|
|
#define ASSERT(b) if(!b) MessageBox(NULL, "FAILED: #b", "ASSERT", MB_OK );
|
|
#else
|
|
#define ASSERT(b)
|
|
#endif
|
|
#endif
|
|
|
|
#define MAX_STRING_RESOURCE_LEN 1024
|
|
LPCSTR g_pszMsgBoxTitle = "HTML Help Dumper Tool";
|
|
|
|
int MsgBox(int idString, UINT nType)
|
|
{
|
|
char szMsg[MAX_STRING_RESOURCE_LEN + 1];
|
|
if (LoadString(GetModuleHandle(NULL), idString, szMsg,
|
|
sizeof(szMsg)) == 0) {
|
|
return 0;
|
|
}
|
|
return MessageBox(GetActiveWindow(), szMsg, g_pszMsgBoxTitle, nType);
|
|
}
|
|
|
|
int MsgBox(PCSTR pszMsg, UINT nType)
|
|
{
|
|
return MessageBox(GetActiveWindow(), pszMsg, g_pszMsgBoxTitle, nType);
|
|
}
|
|
|
|
PCSTR FindFilePortion( PCSTR pszFile )
|
|
{
|
|
PCSTR psz = strrchr(pszFile, '\\');
|
|
if (psz)
|
|
pszFile = psz + 1;
|
|
psz = strrchr(pszFile, '/');
|
|
if (psz)
|
|
return psz + 1;
|
|
psz = strrchr(pszFile, ':');
|
|
return (psz ? psz + 1 : pszFile);
|
|
}
|
|
|
|
typedef enum { JAN=1, FEB, MAR, APR, MAY, JUN, JUL, AUG, SEP, OCT, NOV, DEC } MONTHS;
|
|
|
|
int DaysInMonth(int nMonth, int nYear)
|
|
{
|
|
switch( nMonth ) {
|
|
case SEP: case APR: case JUN: case NOV:
|
|
return 30;
|
|
|
|
case FEB:
|
|
return (nYear % 4) == 0 ? 29 : 28; // handle leap year
|
|
|
|
default:
|
|
return 31;
|
|
}
|
|
}
|
|
|
|
int JulianDate(int nDay, int nMonth, int nYear)
|
|
{
|
|
int nDayOfYear = 0;
|
|
int iMonth;
|
|
|
|
for( iMonth = JAN ; iMonth < nMonth ; iMonth++ )
|
|
nDayOfYear += DaysInMonth(iMonth, nYear);
|
|
|
|
return( (nYear % 10) * 1000 + nDayOfYear + nDay );
|
|
}
|
|
|
|
#define YRMASK 0xFE00
|
|
#define YRSHIFT 9
|
|
|
|
#define MONMASK 0x01E0
|
|
#define MONSHIFT 5
|
|
|
|
#define DAYMASK 0x001F
|
|
#define DAYSHIFT 0
|
|
|
|
#define HRMASK 0xF800
|
|
#define HRSHIFT 11
|
|
#define MINMASK 0x07E0
|
|
#define MINSHIFT 5
|
|
#define SECMASK 0x001F
|
|
#define SECSHIFT 0
|
|
|
|
HRESULT FileTimeToDateTimeString( FILETIME FileTime, LPTSTR pszDateTime )
|
|
{
|
|
HRESULT hr = S_FALSE;
|
|
|
|
WORD wDosDate = 0;
|
|
WORD wDosTime = 0;
|
|
if( FileTimeToDosDateTime( &FileTime, &wDosDate, &wDosTime ) ) {
|
|
DWORD dwDay = (wDosDate & DAYMASK) >> DAYSHIFT;
|
|
DWORD dwMonth = (wDosDate & MONMASK) >> MONSHIFT;
|
|
DWORD dwYear = (((wDosDate & YRMASK) >> YRSHIFT) + 80) % 100;
|
|
DWORD dwHour = (wDosTime & HRMASK) >> HRSHIFT;
|
|
DWORD dwMinute = (wDosTime & MINMASK) >> MINSHIFT;
|
|
DWORD dwSecond = ((wDosTime & SECMASK) >> SECSHIFT) * 2;
|
|
LPCSTR pszAMPM = NULL;
|
|
|
|
if( dwHour >= 12 )
|
|
pszAMPM = "PM";
|
|
else
|
|
pszAMPM = "AM";
|
|
if( dwHour > 12 )
|
|
dwHour -= 12;
|
|
if( dwHour == 0 )
|
|
dwHour = 12;
|
|
|
|
wsprintf( pszDateTime, "%02d/%02d/%02d %02d:%02d %s",
|
|
dwMonth, dwDay, dwYear, dwHour, dwMinute, pszAMPM );
|
|
|
|
hr = S_OK;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
int FileTimeToJulianDate( FILETIME FileTime )
|
|
{
|
|
int iReturn = 0;
|
|
|
|
WORD wDosDate = 0;
|
|
WORD wDosTime = 0;
|
|
if( FileTimeToDosDateTime( &FileTime, &wDosDate, &wDosTime ) ) {
|
|
DWORD dwDay = (wDosDate & DAYMASK) >> DAYSHIFT;
|
|
DWORD dwMonth = (wDosDate & MONMASK) >> MONSHIFT;
|
|
DWORD dwYear = ((wDosDate & YRMASK) >> YRSHIFT) + 1980;
|
|
iReturn = JulianDate( dwDay, dwMonth, dwYear );
|
|
}
|
|
|
|
return iReturn;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////
|
|
//
|
|
// Get the windows directory for the system or the user
|
|
//
|
|
// Note, Windows NT Terminal Server has changed the system API
|
|
// of GetWindowsDirectory to return a per-user system directory.
|
|
// Inorder to determine this condtion we need to check kernel32
|
|
// for the GetSystemWindowsDirectory API and if it exists use
|
|
// this one instead.
|
|
//
|
|
UINT HHGetWindowsDirectory( LPSTR lpBuffer, UINT uSize, UINT uiType )
|
|
{
|
|
UINT uiReturn = 0;
|
|
PFN_GETWINDOWSDIRECTORY pfnGetUsersWindowsDirectory = NULL;
|
|
PFN_GETWINDOWSDIRECTORY pfnGetSystemWindowsDirectory = NULL;
|
|
|
|
// determine which system API to call for each case
|
|
HINSTANCE hInst = LoadLibrary( "Kernel32" );
|
|
if( !hInst )
|
|
return uiReturn;
|
|
|
|
pfnGetSystemWindowsDirectory = (PFN_GETWINDOWSDIRECTORY) GetProcAddress( hInst, "GetSystemWindowsDirectoryA" );
|
|
pfnGetUsersWindowsDirectory = (PFN_GETWINDOWSDIRECTORY) GetProcAddress( hInst, "GetWindowsDirectoryA" );
|
|
ASSERT( pfnGetUsersWindowsDirectory ); // if NULL then we have a bug!
|
|
|
|
if( !pfnGetSystemWindowsDirectory ) {
|
|
pfnGetSystemWindowsDirectory = pfnGetUsersWindowsDirectory;
|
|
}
|
|
|
|
|
|
if( uiType == HH_SYSTEM_WINDOWS_DIRECTORY )
|
|
uiReturn = pfnGetSystemWindowsDirectory( lpBuffer, uSize );
|
|
else if( uiType == HH_USERS_WINDOWS_DIRECTORY )
|
|
uiReturn = pfnGetUsersWindowsDirectory( lpBuffer, uSize );
|
|
else
|
|
uiReturn = 0;
|
|
|
|
FreeLibrary( hInst );
|
|
return uiReturn;
|
|
}
|
|
|
|
LPSTR CatPath(LPSTR lpTop, LPCSTR lpTail)
|
|
{
|
|
//
|
|
// make sure we have a slash at the end of the first element
|
|
//
|
|
LPSTR p;
|
|
|
|
p = lpTop + strlen(lpTop);
|
|
p = CharPrev(lpTop,p);
|
|
if (*p != '\\' && *p != '/')
|
|
{
|
|
strcat(lpTop,"\\");
|
|
}
|
|
|
|
//
|
|
// strip any leading slash from the second element
|
|
//
|
|
|
|
while (*lpTail == '\\') lpTail = CharNext(lpTail);
|
|
|
|
//
|
|
// add them together
|
|
//
|
|
|
|
strcat(lpTop, lpTail);
|
|
|
|
return lpTop;
|
|
}
|
|
|
|
|
|
#pragma data_seg(".text", "CODE")
|
|
|
|
static const char txtGlobal[] = "global.col";
|
|
static const char txtColReg[] = "hhcolreg.dat";
|
|
static const char txtHelp[] = "help";
|
|
static const char txtHHDat[] = "hh.dat";
|
|
|
|
#pragma data_seg()
|
|
|
|
///////////////////////////////////////////////////////////
|
|
//
|
|
// Get the help directory
|
|
//
|
|
// Note, this is always relative to the system's windows
|
|
// directory and not the user's windows directory.
|
|
// See HHGetWindowsDirectory for details on this.
|
|
//
|
|
UINT HHGetHelpDirectory( LPTSTR lpBuffer, UINT uSize )
|
|
{
|
|
UINT uiReturn = 0;
|
|
|
|
uiReturn = HHGetWindowsDirectory( lpBuffer, uSize );
|
|
CatPath( lpBuffer, txtHelp );
|
|
|
|
return uiReturn;
|
|
}
|
|
|
|
DWORD CreatePath(char *szPath)
|
|
{
|
|
char szTmp[MAX_PATH],*p,*q,szTmp2[MAX_PATH];
|
|
DWORD dwErr;
|
|
|
|
strcpy(szTmp2,szPath);
|
|
memset(szTmp,0,sizeof(szTmp));
|
|
q = szTmp2;
|
|
p = szTmp;
|
|
|
|
while (*q)
|
|
{
|
|
if (*q == '/' || *q == '\\')
|
|
{
|
|
if (szTmp[1] == ':' && strlen(szTmp) <= 3)
|
|
{
|
|
if(IsDBCSLeadByte(*q))
|
|
{
|
|
*p++ = *q++;
|
|
if(*q)
|
|
*p++ = *q++;
|
|
}
|
|
else
|
|
*p++ = *q++;
|
|
continue;
|
|
}
|
|
if (!::CreateDirectory(szTmp,0))
|
|
{
|
|
if ( (dwErr = GetLastError()) != ERROR_ALREADY_EXISTS)
|
|
return(dwErr);
|
|
}
|
|
}
|
|
if(IsDBCSLeadByte(*q))
|
|
{
|
|
*p++ = *q++;
|
|
if(*q)
|
|
*p++ = *q++;
|
|
}
|
|
else
|
|
*p++ = *q++;
|
|
}
|
|
if (!::CreateDirectory(szTmp,0))
|
|
{
|
|
if ((dwErr = GetLastError()) != ERROR_ALREADY_EXISTS)
|
|
return(dwErr);
|
|
}
|
|
|
|
return(FALSE);
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////
|
|
//
|
|
// Get the full pathname to the global collections file
|
|
//
|
|
// Note, this is in always in the system's help directory.
|
|
//
|
|
UINT HHGetGlobalCollectionPathname( LPTSTR lpBuffer, UINT uSize, BOOL *pbNewPath )
|
|
{
|
|
UINT uiReturn = 0;
|
|
|
|
*pbNewPath = TRUE;
|
|
uiReturn = HHGetHelpDataPath( lpBuffer );
|
|
|
|
if (uiReturn != S_OK)
|
|
{
|
|
*pbNewPath = FALSE;
|
|
uiReturn = HHGetHelpDirectory( lpBuffer, uSize );
|
|
if( !IsDirectory(lpBuffer) )
|
|
CreatePath( lpBuffer );
|
|
}
|
|
CatPath( lpBuffer, txtColReg );
|
|
|
|
return uiReturn;
|
|
}
|
|
|
|
BOOL IsDirectory( LPCSTR lpszPathname )
|
|
{
|
|
DWORD dwAttribs = GetFileAttributes( lpszPathname );
|
|
if( dwAttribs != (DWORD) -1 )
|
|
if( dwAttribs & FILE_ATTRIBUTE_DIRECTORY )
|
|
return TRUE;
|
|
return FALSE;
|
|
}
|
|
|
|
static const char txtProfiles[] = "Profiles";
|
|
static const char txtUser[] = "Default User";
|
|
static const char txtAllUsers[] = "All Users";
|
|
static const char txtApplicationData[] = "Application Data";
|
|
static const char txtMicrosoft[] = "Microsoft";
|
|
static const char txtHTMLHelp[] = "HTML Help";
|
|
typedef HRESULT (WINAPI *PFN_SHGETFOLDERPATH)( HWND hWnd, int nFolder, HANDLE hToken, DWORD dwFlags, LPTSTR pszPath );
|
|
#ifndef CSIDL_FLAG_CREATE
|
|
#define CSIDL_COMMON_APPDATA 0x0023
|
|
#define CSIDL_FLAG_CREATE 0x8000
|
|
#endif
|
|
|
|
|
|
///////////////////////////////////////////////////////////
|
|
//
|
|
// Get the full path to where the common help data files lives
|
|
// hhcolreg.dat, *.chw and *.chs
|
|
//
|
|
// Note, if the subdirectories of the path does not exist
|
|
// we will create them
|
|
//
|
|
HRESULT HHGetHelpDataPath( LPSTR pszPath )
|
|
{
|
|
HRESULT hResult = S_OK;
|
|
PFN_SHGETFOLDERPATH pfnSHGetFolderPath = NULL;
|
|
|
|
HINSTANCE hInst = LoadLibrary( "Shell32" );
|
|
if( !hInst )
|
|
return S_FALSE;
|
|
|
|
pfnSHGetFolderPath = (PFN_SHGETFOLDERPATH) GetProcAddress( hInst, "SHGetFolderPathA" );
|
|
|
|
// if this function does not exist then we need to similate the return path of
|
|
// "%windir%\Profiles\All Users\Application Data"
|
|
if( pfnSHGetFolderPath ) {
|
|
// now call it
|
|
hResult = pfnSHGetFolderPath( NULL, CSIDL_FLAG_CREATE | CSIDL_COMMON_APPDATA, NULL, 0, pszPath);
|
|
if (pszPath[0] == NULL)
|
|
{
|
|
FreeLibrary( hInst ); // since we already have a copy of Shell32 loaded, free it
|
|
return S_FALSE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
FreeLibrary( hInst ); // since we already have a copy of Shell32 loaded, free it
|
|
return S_FALSE;
|
|
}
|
|
FreeLibrary( hInst ); // since we already have a copy of Shell32 loaded, free it
|
|
|
|
// append "Microsoft"
|
|
CatPath( pszPath, txtMicrosoft );
|
|
if( !IsDirectory(pszPath) )
|
|
if( !CreateDirectory( pszPath, NULL ) )
|
|
return S_FALSE;
|
|
|
|
// append "HTML Help"
|
|
CatPath( pszPath, txtHTMLHelp );
|
|
if( !IsDirectory(pszPath) )
|
|
if( !CreateDirectory( pszPath, NULL ) )
|
|
return S_FALSE;
|
|
|
|
return hResult;
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////
|
|
//
|
|
// Get the full pathname to the user's data file
|
|
//
|
|
// Note, this is always relative to the users's windows
|
|
// directory and not the system's windows directory.
|
|
// See HHGetWindowsDirectory for details on this.
|
|
//
|
|
UINT HHGetUsersDataPathname( LPTSTR lpBuffer, UINT uSize )
|
|
{
|
|
UINT uiReturn = 0;
|
|
|
|
uiReturn = HHGetWindowsDirectory( lpBuffer, uSize, HH_USERS_WINDOWS_DIRECTORY );
|
|
CatPath( lpBuffer, txtHHDat );
|
|
|
|
return uiReturn;
|
|
}
|