#define	STRICT
#include	<windows.h>
#include	<windowsx.h>
#include	<string.h>
#include	<tchar.h>
#include	<commdlg.h>
#include	"resource.h"
#include	"tools.h"
#include	"import.h"
#include	"convert.h"
#include	"fileopen.h"

extern	HWND		hMainWnd ;
extern	HINSTANCE	hInst ;

static	TCHAR	UnicodeBuf[ 2048 ] ;
static	BYTE	SjisBuf[ 2048 ] ;
static	OPENFILENAME	ofn ;
static	TCHAR	buf1[ MAX_PATH + 1 ] ;
static	TCHAR	Filename[ MAX_PATH ] ;

const	TCHAR	HexStr[] = TEXT("0123456789ABCDEFabcdef") ;

BOOL
ImportDlg( HWND hWnd, LPTSTR buf )
{
	memset( &ofn, 0, sizeof ofn ) ;
	memcpy( buf1, TEXT("*.reg\0"), sizeof TEXT("*.reg\0") ) ;
	ofn.lStructSize = sizeof ofn ;
    ofn.hwndOwner = hWnd ;
    ofn.hInstance = hInst ;
    ofn.lpstrFilter = TEXT("Registry Files (*.reg)\0*.reg\0All Files (*.*)\0*.*\0\0") ;
    ofn.lpstrCustomFilter = NULL ;
    ofn.nMaxCustFilter = 0 ;
    ofn.nFilterIndex = 1 ;
    ofn.lpstrFile = buf1 ;
    ofn.nMaxFile = MAX_PATH + 1 ;
    ofn.lpstrFileTitle = NULL ;
    ofn.nMaxFileTitle = 0 ;
    ofn.lpstrInitialDir = NULL ;
    ofn.lpstrTitle = NULL ;
    ofn.Flags = 0 ;
    ofn.nFileOffset = 0 ;
    ofn.nFileExtension = 0 ;
    ofn.lpstrDefExt = TEXT("REG") ;
    ofn.lCustData = 0 ;
    ofn.lpfnHook = NULL ;
    ofn.lpTemplateName = NULL ;
	if ( !GetOpenFileName( &ofn ) ) {
		return FALSE ;
	}
	_tcscpy( buf, buf1 ) ;
	return TRUE ;
}

static	BOOL
ExportDlg( HWND hWnd, LPTSTR buf )
{
	memset( &ofn, 0, sizeof ofn ) ;
	memcpy( buf1, TEXT("*.reg\0"), sizeof TEXT("*.reg\0") ) ;
	ofn.lStructSize = sizeof ofn ;
    ofn.hwndOwner = hWnd ;
    ofn.hInstance = hInst ;
    ofn.lpstrFilter = TEXT("Registry Files (*.reg)\0*.reg\0All Files (*.*)\0*.*\0\0") ;
    ofn.lpstrCustomFilter = NULL ;
    ofn.nMaxCustFilter = 0 ;
    ofn.nFilterIndex = 1 ;
    ofn.lpstrFile = buf1 ;
    ofn.nMaxFile = MAX_PATH + 1 ;
    ofn.lpstrFileTitle = NULL ;
    ofn.nMaxFileTitle = 0 ;
    ofn.lpstrInitialDir = NULL ;
    ofn.lpstrTitle = NULL ;
    ofn.Flags = 0 ;
    ofn.nFileOffset = 0 ;
    ofn.nFileExtension = 0 ;
    ofn.lpstrDefExt = TEXT("REG") ;
    ofn.lCustData = 0 ;
    ofn.lpfnHook = NULL ;
    ofn.lpTemplateName = NULL ;
	if ( !GetSaveFileName( &ofn ) ) {
		return FALSE ;
	}
	_tcscpy( buf, buf1 ) ;
	return TRUE ;
}

DWORD
hex2num( LPCTSTR str )
{
	TCHAR	c ;
	DWORD	result = 0 ;

	while ( c = *str++ ) {
		if ( !_tcschr( HexStr, c ) ) {
			break ;
		}
		result *= 16 ;
		if ( c >= TEXT('a') && c <= TEXT('f') ) {
			result += c - 'a' + 10 ;
		} else if ( c >= TEXT('A') && c <= TEXT('F') ) {
			result += c - 'A' + 10 ;
		} else {
			result += c - '0' ;
		}
	}
	return result ;
}

BOOL
parse_str( LPTSTR dst, LPDWORD pSize, LPCTSTR *pSrc, TCHAR last )
{
	DWORD	size = 0 ;
	TCHAR	c, buf[ 3 ] ;
	LPCTSTR	src = *pSrc ;
	BOOL	f_quote = FALSE ;

	while ( c = *src++ ) {
		if ( f_quote ) {
			if ( c == TEXT('\\') ) {
				c = *src++ ;
				if ( !c ) {
					return FALSE ;
				} else if ( c == TEXT('x') ) {
					buf[0] = *src++ ;
					buf[1] = *src++ ;
					buf[2] = 0 ;
					if ( _tcslen( buf ) != 2 ) {
						return FALSE ;
					}
					c = (TCHAR) hex2num( buf ) ;
				}
			} else if ( c == TEXT('"') ) {
				f_quote = FALSE ;
				continue ;
			}
		} else {
			if ( c == TEXT('"') ) {
				f_quote = TRUE ;
				continue ;
			} else if ( c == last ) {
				break ;
			}
		}
		*dst++ = c ;
		size ++ ;
	}
	if ( c != last ) {
		return FALSE ;
	}
	*dst = 0 ;
	size ++ ;
	*pSrc = src ;
	if ( pSize ) {
		*pSize = size * sizeof (TCHAR) ;
	}
	return TRUE ;
}

BOOL
parse_hex( LPBYTE dst, LPDWORD pSize, LPCTSTR src )
{
	DWORD	size = 0 ;
	TCHAR	c, buf[ 3 ] ;

	while ( c = *src++ ) {
		buf[0] = c ;
		buf[1] = *src++ ;
		buf[2] = 0 ;
		if ( !buf[1] ) {
			return FALSE ;
		}
		*dst++ = (BYTE) hex2num( buf ) ;
		size ++ ;
		c = *src++ ;
		if ( c != TEXT(',') ) {
			break ;
		}
	}
	if ( c ) {
		return FALSE ;
	}
	if ( pSize ) {
		*pSize = size ;
	}
	return TRUE ;
}

BOOL
parse_reg( LPCTSTR line )
{
	LPCTSTR	ptr ;
	DWORD	size ;
	TCHAR	buf[ 20 ] ;

	/* レジストリ名を取得する */
	if ( !parse_str( ValueName, NULL, &line, TEXT('=') ) ) {
		return FALSE ;
	}
	if ( !_tcsnicmp( line, TEXT("DWORD:"), 6 ) ) {
		ValueType = REG_DWORD ;
		*(LPDWORD)ValueData = hex2num( &line[ 6 ] ) ;
		ValueSize = sizeof (DWORD) ;
		return TRUE ;
	} else if ( !_tcsnicmp( line, TEXT("HEX("), 4 ) ) {
		ptr = _tcschr( line, TEXT(')') ) ;
		if ( !ptr ) {
			return FALSE ;
		}
		size = (DWORD) (ptr - line) ;
		if ( (size + 1) * sizeof (TCHAR) > sizeof buf ) {
			return FALSE ;
		}
		memcpy( buf, &line[ 4 ], size * sizeof (TCHAR) ) ;
		buf[ size ] = 0 ;
		ValueType = _ttol( buf ) ;
		line = ptr + 1 ;
		if ( *line++ != TEXT(':') ) {
			return FALSE ;
		}
		return parse_hex( ValueData, &ValueSize, line ) ;
	} else {
		ValueType = REG_SZ ;
		return parse_str( (LPTSTR) ValueData, &ValueSize, &line, 0 ) ;
	}
}

BOOL
import_reg( LPCTSTR fn )
{
	LONG	lret ;
	DWORD	disposition ;
	LPTSTR	subkey, ptr ;
	HKEY	hKeyRoot, hKey = 0 ;

	if ( !Fopen( fn, TEXT("r") ) ) {
		return FALSE ;
	}
	while ( Fgets( SjisBuf, sizeof SjisBuf ) ) {
		SjisToUnicode( SjisBuf, UnicodeBuf, sizeof UnicodeBuf ) ;
		ptr = _tcsrchr( UnicodeBuf, TEXT('\n') ) ;
		if ( ptr ) {
			*ptr = 0 ;
		}
		if ( !*UnicodeBuf ) {
			continue ;
		} else if ( *UnicodeBuf == '[' ) {
			if ( hKey ) {
				RegCloseKey( hKey ) ;
				hKey = 0 ;
			}
			subkey = _tcschr( UnicodeBuf, TEXT('\\') ) ;
			if ( !subkey ) {
				continue ;
			}
			*subkey++ = 0 ;
			ptr = _tcsrchr( subkey, TEXT(']') ) ;
			if ( !ptr ) {
				continue ;
			}
			*ptr = 0 ;
			if ( !_tcsicmp( &UnicodeBuf[1], TEXT("HKEY_LOCAL_MACHINE") ) ) {
				hKeyRoot = HKEY_LOCAL_MACHINE ;
			} else if ( !_tcsicmp( &UnicodeBuf[1], TEXT("HKEY_CURRENT_USER") ) ) {
				hKeyRoot = HKEY_CURRENT_USER ;
			} else if ( !_tcsicmp( &UnicodeBuf[1], TEXT("HKEY_USERS") ) ) {
				hKeyRoot = HKEY_USERS ;
			} else if ( !_tcsicmp( &UnicodeBuf[1], TEXT("HKEY_CLASSES_ROOT") ) ) {
				hKeyRoot = HKEY_CLASSES_ROOT ;
			} else {
				continue ;
			}
			/* Keyを作成する */
			lret = RegCreateKeyEx( hKeyRoot, subkey, 0, TEXT(""), 0,
#ifdef	_WIN32_WCE
									0,
#else	/* _WIN32_WCE */
									KEY_ALL_ACCESS,
#endif	/* _WIN32_WCE */
									NULL, &hKey, &disposition ) ;
			if ( lret != ERROR_SUCCESS ) {
				DWORD	ret = GetLastError() ;
				MessageBox( hMainWnd, TEXT("can't create the key"), subkey, MB_OK ) ;
				hKey = 0 ;
				continue ;
			}
		} else if ( hKey ) {
			parse_reg( UnicodeBuf ) ;
			lret = RegSetValueEx( hKey, ValueName, 0, ValueType, ValueData, ValueSize ) ;
			if ( lret != ERROR_SUCCESS ) {
				MessageBox( hMainWnd, TEXT("can't write the value"), ValueName, MB_OK ) ;
				continue ;
			}
		}
	}
	if ( hKey ) {
		RegCloseKey( hKey ) ;
	}
	Fclose() ;
	return TRUE ;
}

typedef struct list {
	LPTSTR	subkey ;
	struct list	*next ;
} List, *LPList ;

LPList		Root = 0 ;
HANDLE		hHeap = 0 ;

void
push( LPCTSTR subkey )
{
	LPList	newlist ;

	newlist = HeapAlloc( hHeap, HEAP_ZERO_MEMORY, sizeof (List) ) ;
	if ( !newlist ) {
		return ;
	}
	newlist->subkey = HeapAlloc( hHeap, HEAP_ZERO_MEMORY, (_tcslen( subkey ) + 1) * sizeof (TCHAR) ) ;
	if ( !newlist->subkey ) {
		HeapFree( hHeap, 0, newlist ) ;
		return ;
	}
	_tcscpy( newlist->subkey, subkey ) ;
	newlist->next = Root ;
	Root = newlist ;
}

BOOL
pop( LPTSTR subkey )
{
	LPList	todelete ;

	if ( !Root ) {
		return FALSE ;
	}
	todelete = Root ;
	Root = Root->next ;
	_tcscpy( subkey, todelete->subkey ) ;
	HeapFree( hHeap, 0, todelete->subkey ) ;
	HeapFree( hHeap, 0, todelete ) ;
	return TRUE ;
}

void
Fputs_quoted( LPCTSTR str )
{
	TCHAR	c ;
	LPTSTR	dst = UnicodeBuf ;

	*dst++ = TEXT('"') ;
	while ( c = *str++ ) {
		if ( c == TEXT('"') || c == TEXT('\\') ) {
			*dst++ = TEXT('\\') ;
		} else if ( c < TEXT(' ') ) {
			wsprintf( dst, TEXT("\\x%02X"), c ) ;
			dst += 4 ;
			continue ;
		}
		*dst++ = c ;
	}
	*dst++ = TEXT('"') ;
	*dst = 0 ;
	UnicodeToSjis( UnicodeBuf, SjisBuf, sizeof SjisBuf ) ;
	Fputs( SjisBuf ) ;
}

BOOL
export_reg_sub( HKEY root, LPCTSTR rootkey )
{
	HKEY	hKey ;
	LONG	lret ;
	LPCTSTR	rootname ;
	TCHAR	subkey[ MAX_PATH ] ;
	DWORD	index, cchName, cbData, i ;

	if ( root == HKEY_LOCAL_MACHINE ) {
		rootname = TEXT("HKEY_LOCAL_MACHINE") ;
	} else if ( root == HKEY_CURRENT_USER ) {
		rootname = TEXT("HKEY_CURRENT_USER") ;
	} else if ( root == HKEY_USERS ) {
		rootname = TEXT("HKEY_USERS") ;
	} else if ( root == HKEY_CLASSES_ROOT ) {
		rootname = TEXT("HKEY_CLASSES_ROOT") ;
	} else {
		return FALSE ;
	}
	push( rootkey ) ;
	while ( pop( subkey ) ) {
		wsprintf( UnicodeBuf, TEXT("\n[%s\\%s]\n"), rootname, subkey ) ;
		UnicodeToSjis( UnicodeBuf, SjisBuf, sizeof SjisBuf ) ;
		Fputs( SjisBuf ) ;

		lret = RegOpenKeyEx( root, subkey, 0,
#ifdef	_WIN32_WCE
							0,
#else	/* _WIN32_WCE */
							KEY_ALL_ACCESS,
#endif	/* _WIN32_WCE */
							&hKey ) ;
		if ( lret != ERROR_SUCCESS ) {
			continue ;
		}
		index = 0 ;
		while ( 1 ) {
			cchName = sizeof ValueName / sizeof (TCHAR) ;
			cbData = sizeof ValueData ;
			lret = RegEnumValue( hKey, index ++,
								 ValueName, &cchName,
								 NULL, &ValueType,
								 ValueData, &cbData ) ;
			if ( lret != ERROR_SUCCESS ) {
				break ;
			}
			Fputs_quoted( ValueName ) ;
			Fputc( '=' ) ;
			if ( ValueType == REG_DWORD ) {
				Fputs( "dword:" ) ;
				wsprintf( UnicodeBuf, TEXT("%08X"), *(LPDWORD)ValueData ) ;
				UnicodeToSjis( UnicodeBuf, SjisBuf, sizeof SjisBuf ) ;
				Fputs( SjisBuf ) ;
			} else if ( ValueType == REG_SZ ) {
				Fputs_quoted( (LPCTSTR)ValueData ) ;
			} else {
				wsprintf( UnicodeBuf, TEXT("hex(%d):"), ValueType ) ;
				UnicodeToSjis( UnicodeBuf, SjisBuf, sizeof SjisBuf ) ;
				Fputs( SjisBuf ) ;
				for ( i = 0 ; i < cbData ; i ++ ) {
					if ( i ) {
						Fputc( ',' ) ;
					}
					wsprintf( UnicodeBuf, TEXT("%02x"), ValueData[i] ) ;
					UnicodeToSjis( UnicodeBuf, SjisBuf, sizeof SjisBuf ) ;
					Fputs( SjisBuf ) ;
				}
			}
			Fputc( '\n' ) ;
		}
		index = 0 ;
		while ( 1 ) {
			cchName = sizeof KeyName / sizeof (TCHAR) ;
			lret = RegEnumKeyEx( hKey, index ++,
								 KeyName, &cchName,
								 NULL, NULL, NULL, NULL ) ;
			if ( lret != ERROR_SUCCESS ) {
				break ;
			} else if ( !*KeyName ) {
				continue ;
			}
			_tcscpy( UnicodeBuf, subkey ) ;
			if ( *UnicodeBuf ) {
				_tcscat( UnicodeBuf, TEXT("\\") ) ;
			}
			_tcscat( UnicodeBuf, KeyName ) ;
			push( UnicodeBuf ) ;
		}
		RegCloseKey( hKey ) ;
	}
	Fputc( '\n' ) ;
	return TRUE ;
}

BOOL
export_reg( HKEY root, LPCTSTR subkey )
{
	BOOL	ret = FALSE ;

	if ( !ExportDlg( hMainWnd, Filename ) ) {
		return FALSE ;
	}
	if ( Fexist( Filename ) ) {
		if ( MessageBox( hMainWnd, TEXT("Replace existing file?"), TEXT("File Already Exists"), MB_OKCANCEL ) != IDOK ) {
			return FALSE ;
		}
	}
	hHeap = HeapCreate( 0, 1024, 0 ) ;
	if ( hHeap == NULL ) {
		return FALSE ;
	}
	if ( !Fopen( Filename, TEXT("w") ) ) {
		goto exit ;
	}
	ret = export_reg_sub( root, subkey ) ;
	Fclose() ;

exit:
	HeapDestroy( hHeap ) ;
	hHeap = 0 ;
	return ret ;
}
