; A DDraw app that enumerates all available modes and allows you to test them
; Features : DDraw change resolution, Callback function, Tooltips
; Coded by promethee (promethee@voila.fr)
; Thanx to all my friends and all those who helped me
; Often hanging in #win32asm, #c4n, #cracking, #reversing4newbies

.386
.MODEL FLAT, STDCALL

option casemap :none

INCLUDE \masm32\include\windows.inc

INCLUDE ddraw.inc
INCLUDE \masm32\include\gdi32.inc
INCLUDE \masm32\include\kernel32.inc
INCLUDE \masm32\include\user32.inc
INCLUDE \masm32\include\comctl32.inc

includelib \masm32\lib\gdi32.lib
includelib \masm32\lib\ddraw.lib
includelib \masm32\lib\kernel32.lib
includelib \masm32\lib\user32.lib
includelib \masm32\lib\comctl32.lib

.DATA
lpDD			LPDIRECTDRAW	?											; Direct Draw structure :
szCaption       db	"Assembly Win32 sample program", 0						; read the EXCELLENT tute by my friend
szText          db	"This is our first program under Windows 95", 13, 10	; X-CALIBRE :o)
                db	"made in MASM 6.1"
Hexa			db	"0123456789ABCDEF", 0
szList			db	"LISTBOX", 0
szToolTip		db	"tooltips_class32", 0
szDisplayName	db	"Available video modes", 0
szTTMsg			db	"Double-click to test mode", 0
szCurrMode		db	"Current mode", 0

.DATA?
hInst			dd				?
lpfnWndProc		dd				?
hWnd			dd				?
hWndToolTip		dd				? 
msg				MSG				<>
lpti			TOOLINFO		<>
DDSurfaceDesc	DDSURFACEDESC	<>
ModesCount		dd				?
VideoModes		dd				3*50 dup (?)

.CODE
;***************
;FORMATTING CRAP
;***************
WidthHeight	MACRO			; Macro to change width and height int values into decimal strings

	.REPEAT
		xor		edx, edx
		div		ebx
		or		dl, '0'
		mov		[edi], dl
		dec		edi
	.UNTILCXZ

ENDM

FormatMode	MACRO 			; Macro to create the Width*Height*BPP string from the int values

	mov		byte ptr [edi+1], 0

	mov		ebx, 10
	xor		edx, edx

	mov		eax, [esi.DDSURFACEDESC.ddpfPixelFormat.dwRGBBitCount]
	.IF eax < 10
		xor		edx, edx			; Avoid "Division Error"
		div		ebx
		or		dl, '0'
		mov		[edi], dl
		dec		edi

		xor		edx, edx
		div		ebx
		mov		dl, ' '				; Don't put a preceding 0, but a space
		mov		[edi], dl
		dec		edi
	.ELSE
		mov		ecx, 2
		.REPEAT
			xor		edx, edx
			div		ebx
			or		dl, '0'
			mov		[edi], dl
			dec		edi
		.UNTILCXZ
	.ENDIF

	mov		byte ptr [edi], '*'		; Insert a * between BPP and Height
	dec		edi


	mov		eax, [esi.DDSURFACEDESC.dwHeight]
	.IF eax < 1000
		mov		ecx, 3
		WidthHeight
		mov		dl, ' '
		mov		[edi], dl
		dec		edi
	.ELSE
		mov		ecx, 4
		WidthHeight
	.ENDIF


	mov		byte ptr [edi], '*'
	dec		edi

	mov		eax, [esi.DDSURFACEDESC.dwWidth]
	.IF eax < 1000
		mov		ecx, 3
		WidthHeight
		mov		dl, ' '
		mov		[edi], dl
		dec		edi
	.ELSE
		mov		ecx, 4
		WidthHeight
	.ENDIF
	
ENDM

;*****************************
;CALLBACK procedure for DDEnum
;*****************************

EvokeModes	PROC	proc uses ebx edi esi, lpDDSurfaceDesc:DWORD, lpContext:DWORD

LOCAL	modeBuff[30]:BYTE			; Buffer for the string

	mov		esi, [lpDDSurfaceDesc]	; Look in DDRAW.INC
	lea		edi, [modeBuff+11]
	FormatMode						; Width*Height*BPP in the buffer

	mov		ecx, [ModesCount]		; Save modes in a list
	inc		[ModesCount]
	mov		eax, ecx
	shl		ecx, 4
	sub		ecx, eax				; 12*ebx

	mov		eax, [esi.DDSURFACEDESC.dwWidth]
	mov		[VideoModes+ecx], eax
	mov		eax, [esi.DDSURFACEDESC.dwHeight]
	mov		[VideoModes+ecx+4], eax
	mov		eax, [esi.DDSURFACEDESC.ddpfPixelFormat.dwRGBBitCount]
	mov		[VideoModes+ecx+8], eax

	INVOKE	SendMessage, [hWnd], LB_ADDSTRING, NULL, ADDR modeBuff	;Add current mode to the list
	mov		eax, DDENUMRET_OK		; Everything was OK, go on with next mode
	
	ret

EvokeModes	ENDP

;*******************************
;WndProc customised for the list
;*******************************
MyListProc 	PROC hList:DWORD,uMsg:DWORD,wParam:DWORD,lParam:DWORD
LOCAL	Buff[30]:BYTE

	mov		eax, [uMsg]
	.IF eax == WM_DESTROY			; If the window is killed, kill the app as well !
		INVOKE	PostQuitMessage, NULL
		ret
	.ELSEIF eax == WM_MOUSEMOVE		; Relay mouse events to the ToolTip !
		INVOKE	SendMessage, [hWndToolTip], TTM_RELAYEVENT, NULL, ADDR hList
	.ELSEIF	eax == WM_SIZE			; Reset ToolTip action area when windows is resized !
		INVOKE	GetClientRect, [hWnd], ADDR [lpti.rect]
		INVOKE	SendMessage, [hWndToolTip], TTM_NEWTOOLRECT, NULL, ADDR lpti
	.ELSEIF eax == WM_LBUTTONDBLCLK	; On Double-Click, Change mode
		INVOKE	SendMessage, [hWndToolTip], TTM_ACTIVATE, NULL, NULL		; Deactivate
		INVOKE	SendMessage, [hWnd], LB_GETCURSEL, NULL, NULL
		mov		ebx, eax
		shl		eax, 4
		sub		eax, ebx			; 12*eax

		push	eax

; FULLSCREEN is neede to be able to change the color depth
; The DD surface needs to be attached to a window - her the ToolTip :o)
		DDINVOKE	SetCooperativeLevel, lpDD, [hWndToolTip], DDSCL_EXCLUSIVE or DDSCL_FULLSCREEN
		pop		eax
		mov		ebx, [VideoModes+eax]	; Width
		mov		ecx, [VideoModes+eax+4]	; Height
		mov		edx, [VideoModes+eax+8]	; BPP
		DDINVOKE	SetDisplayMode, lpDD, ebx, ecx, edx	; Try and get to new display mode

; Structures need to know their size for compatibility reaseons
		mov		[DDSurfaceDesc.dwSize], sizeof DDSurfaceDesc
		DDINVOKE GetDisplayMode, lpDD, ADDR DDSurfaceDesc	; get new display mode
		
		lea		esi, [DDSurfaceDesc]
		lea		edi, [Buff+11]
		FormatMode

		INVOKE	MessageBox, NULL, ADDR Buff, ADDR szCurrMode , MB_OK	; Display new mode
		DDINVOKE	RestoreDisplayMode, lpDD							; Restore old mode
		INVOKE	SendMessage, [hWndToolTip], TTM_ACTIVATE, 1, NULL		; Reactivate
	.ENDIF
	
;Default action
	INVOKE	CallWindowProc, [lpfnWndProc], [hList], [uMsg], [wParam], [lParam]

	ret

MyListProc	ENDP

;************************
;REAL PROGGY (at last :o)
;************************
Main:
	INVOKE	GetModuleHandle, NULL
	mov		[hInst], eax

	INVOKE	CreateWindowEx, \
		WS_EX_PALETTEWINDOW or WS_EX_CLIENTEDGE, \
		ADDR szList, \
		ADDR szDisplayName, \
		WS_OVERLAPPEDWINDOW or WS_VSCROLL or LBS_HASSTRINGS or LBS_NOINTEGRALHEIGHT or LBS_DISABLENOSCROLL, \
		CW_USEDEFAULT, \
		CW_USEDEFAULT, \
		130, \
		200, \
		NULL, \
		NULL, \
		[hInst], \
		NULL
	mov		[hWnd], eax

        INVOKE	SetWindowLong, [hWnd], GWL_WNDPROC, ADDR MyListProc
        mov 	[lpfnWndProc], eax

        INVOKE	GetStockObject,ANSI_FIXED_FONT				;\ Sorry ripped from POPLIST.ASM
        INVOKE	SendMessage,[hWnd],WM_SETFONT,eax,0			;/ 

	INVOKE	ShowWindow, [hWnd], SW_SHOWNORMAL
	INVOKE	UpdateWindow, [hWnd]

	INVOKE	InitCommonControls								; Needed by the ToolTip
	
	INVOKE	CreateWindowEx, NULL, ADDR szToolTip, ADDR szToolTip,
		WS_POPUP or TTS_NOPREFIX or TTS_ALWAYSTIP,
		CW_USEDEFAULT, CW_USEDEFAULT,
		CW_USEDEFAULT, CW_USEDEFAULT,
		[hWnd], NULL, [hInst], NULL
	mov		[hWndToolTip], eax

	INVOKE	SetWindowPos, [hWndToolTip], HWND_TOPMOST,0, 0, 0, 0,	; Set position as TOPMPST, so we can see it
	SWP_NOMOVE or SWP_NOSIZE or SWP_NOACTIVATE

	mov		[lpti.cbSize], sizeof lpti
	mov		eax, [hWnd]
	mov		[lpti.hWnd], eax
	INVOKE	GetClientRect, [hWnd], ADDR [lpti.rect]					; Get client size of the list
	mov		dword ptr [lpti.uId], 1
	mov		[lpti.lpszText], offset szTTMsg
	INVOKE	SendMessage, [hWndToolTip], TTM_ADDTOOL, NULL, ADDR lpti	; Add a tool attached to the list

	INVOKE	DirectDrawCreate, NULL, ADDR lpDD, NULL					; Create surface
	DDINVOKE	SetCooperativeLevel, lpDD, [hWndToolTip], DDSCL_EXCLUSIVE or DDSCL_FULLSCREEN

	DDINVOKE	EnumDisplayModes, lpDD, NULL, NULL, NULL, offset EvokeModes	; Get modes

	INVOKE	SetFocus, [hWnd]

	.WHILE 1
		INVOKE	GetMessage, ADDR msg,NULL,0,0
		.BREAK .IF (!eax)
		INVOKE	TranslateMessage, ADDR msg
		INVOKE	DispatchMessage, ADDR msg
	.ENDW

end_loop:

	DDINVOKE	Release, lpDD				; Free the object
	INVOKE	ExitProcess, NULL

END	Main
