summaryrefslogtreecommitdiffstatshomepage
path: root/src/osd/windows
diff options
context:
space:
mode:
author Aaron Giles <aaron@aarongiles.com>2007-12-17 15:19:59 +0000
committer Aaron Giles <aaron@aarongiles.com>2007-12-17 15:19:59 +0000
commit7b77f1218624ea26dbb2efd85a19f795f5d4e02e (patch)
tree19209304095572b4fd61c2a2d6a5aa75c4e471ad /src/osd/windows
parent3da7f476068b3ffef713218ba2fc1bd5030f2c38 (diff)
Initial checkin of MAME 0.121.mame0121
Diffstat (limited to 'src/osd/windows')
-rw-r--r--src/osd/windows/d3d8intf.c557
-rw-r--r--src/osd/windows/d3d9intf.c590
-rw-r--r--src/osd/windows/d3dintf.h241
-rw-r--r--src/osd/windows/debugwin.c3025
-rw-r--r--src/osd/windows/debugwin.h32
-rw-r--r--src/osd/windows/drawd3d.c2421
-rw-r--r--src/osd/windows/drawdd.c1435
-rw-r--r--src/osd/windows/drawgdi.c215
-rw-r--r--src/osd/windows/drawnone.c104
-rw-r--r--src/osd/windows/input.c2129
-rw-r--r--src/osd/windows/input.h27
-rw-r--r--src/osd/windows/ledutil.c738
-rw-r--r--src/osd/windows/main.c89
-rw-r--r--src/osd/windows/mame.man10
-rw-r--r--src/osd/windows/mame.rc13
-rw-r--r--src/osd/windows/mamex64.man10
-rw-r--r--src/osd/windows/mamex64.rc14
-rw-r--r--src/osd/windows/multidef.h38
-rw-r--r--src/osd/windows/osd_cpu.h60
-rw-r--r--src/osd/windows/osinline.h562
-rw-r--r--src/osd/windows/output.c311
-rw-r--r--src/osd/windows/output.h97
-rw-r--r--src/osd/windows/sound.c427
-rw-r--r--src/osd/windows/sound.h20
-rw-r--r--src/osd/windows/strconv.c108
-rw-r--r--src/osd/windows/strconv.h37
-rw-r--r--src/osd/windows/vconv.c450
-rw-r--r--src/osd/windows/verinfo.c267
-rw-r--r--src/osd/windows/video.c524
-rw-r--r--src/osd/windows/video.h100
-rw-r--r--src/osd/windows/winalloc.c395
-rw-r--r--src/osd/windows/windir.c155
-rw-r--r--src/osd/windows/window.c1885
-rw-r--r--src/osd/windows/window.h160
-rw-r--r--src/osd/windows/windows.mak299
-rw-r--r--src/osd/windows/winfile.c331
-rw-r--r--src/osd/windows/winmain.c844
-rw-r--r--src/osd/windows/winmain.h68
-rw-r--r--src/osd/windows/winmisc.c65
-rw-r--r--src/osd/windows/winprefix.h35
-rw-r--r--src/osd/windows/winsync.c157
-rw-r--r--src/osd/windows/wintime.c159
-rw-r--r--src/osd/windows/winutf8.c260
-rw-r--r--src/osd/windows/winutf8.h30
-rw-r--r--src/osd/windows/winutil.c125
-rw-r--r--src/osd/windows/winutil.h20
-rw-r--r--src/osd/windows/winwork.c775
47 files changed, 20414 insertions, 0 deletions
diff --git a/src/osd/windows/d3d8intf.c b/src/osd/windows/d3d8intf.c
new file mode 100644
index 00000000000..4b702b33339
--- /dev/null
+++ b/src/osd/windows/d3d8intf.c
@@ -0,0 +1,557 @@
+//============================================================
+//
+// d3d9intf.c - Direct3D 8.1 abstraction layer
+//
+// Copyright (c) 1996-2007, Nicola Salmoria and the MAME Team.
+// Visit http://mamedev.org for licensing and usage restrictions.
+//
+//============================================================
+
+// standard windows headers
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#include <d3d8.h>
+
+// MAME headers
+#include "mame.h"
+
+// MAMEOS headers
+#include "d3dintf.h"
+#include "winmain.h"
+
+
+
+//============================================================
+// TYPE DEFINITIONS
+//============================================================
+
+typedef IDirect3D8 *(WINAPI *direct3dcreate8_ptr)(UINT SDKVersion);
+
+
+
+//============================================================
+// PROTOTYPES
+//============================================================
+
+static void set_interfaces(d3d *d3dptr);
+
+
+
+//============================================================
+// INLINES
+//============================================================
+
+INLINE void convert_present_params(const d3d_present_parameters *params, D3DPRESENT_PARAMETERS *d3d8params)
+{
+ memset(d3d8params, 0, sizeof(*d3d8params));
+ d3d8params->BackBufferWidth = params->BackBufferWidth;
+ d3d8params->BackBufferHeight = params->BackBufferHeight;
+ d3d8params->BackBufferFormat = params->BackBufferFormat;
+ d3d8params->BackBufferCount = params->BackBufferCount;
+ d3d8params->MultiSampleType = params->MultiSampleType;
+// d3d8params->MultiSampleQuality = params->MultiSampleQuality;
+ d3d8params->SwapEffect = params->SwapEffect;
+ d3d8params->hDeviceWindow = params->hDeviceWindow;
+ d3d8params->Windowed = params->Windowed;
+ d3d8params->EnableAutoDepthStencil = params->EnableAutoDepthStencil;
+ d3d8params->AutoDepthStencilFormat = params->AutoDepthStencilFormat;
+ d3d8params->Flags = params->Flags;
+ d3d8params->FullScreen_RefreshRateInHz = params->FullScreen_RefreshRateInHz;
+ d3d8params->FullScreen_PresentationInterval = params->PresentationInterval;
+ if (d3d8params->Windowed)
+ d3d8params->FullScreen_PresentationInterval = 0;
+}
+
+
+
+//============================================================
+// drawd3d8_init
+//============================================================
+
+d3d *drawd3d8_init(void)
+{
+ direct3dcreate8_ptr direct3dcreate8;
+ HINSTANCE dllhandle;
+ IDirect3D8 *d3d8;
+ d3d *d3dptr;
+
+ // dynamically grab the create function from d3d8.dll
+ dllhandle = LoadLibrary(TEXT("d3d8.dll"));
+ if (dllhandle == NULL)
+ {
+ mame_printf_verbose("Direct3D: Unable to access d3d8.dll\n");
+ return NULL;
+ }
+
+ // import the create function
+ direct3dcreate8 = (direct3dcreate8_ptr)GetProcAddress(dllhandle, "Direct3DCreate8");
+ if (direct3dcreate8 == NULL)
+ {
+ mame_printf_verbose("Direct3D: Unable to find Direct3DCreate8\n");
+ FreeLibrary(dllhandle);
+ dllhandle = NULL;
+ return NULL;
+ }
+
+ // create our core direct 3d object
+ d3d8 = (*direct3dcreate8)(D3D_SDK_VERSION);
+ if (d3d8 == NULL)
+ {
+ mame_printf_verbose("Direct3D: Error attempting to initialize Direct3D8\n");
+ FreeLibrary(dllhandle);
+ dllhandle = NULL;
+ return NULL;
+ }
+
+ // allocate an object to hold our data
+ d3dptr = malloc_or_die(sizeof(*d3dptr));
+ d3dptr->version = 8;
+ d3dptr->d3dobj = d3d8;
+ d3dptr->dllhandle = dllhandle;
+ set_interfaces(d3dptr);
+
+ mame_printf_verbose("Direct3D: Using Direct3D 8\n");
+ return d3dptr;
+}
+
+
+
+//============================================================
+// Direct3D interfaces
+//============================================================
+
+static HRESULT d3d_check_device_format(d3d *d3dptr, UINT adapter, D3DDEVTYPE devtype, D3DFORMAT adapterformat, DWORD usage, D3DRESOURCETYPE restype, D3DFORMAT format)
+{
+ IDirect3D8 *d3d8 = (IDirect3D8 *)d3dptr->d3dobj;
+ return IDirect3D8_CheckDeviceFormat(d3d8, adapter, devtype, adapterformat, usage, restype, format);
+}
+
+
+static HRESULT d3d_check_device_type(d3d *d3dptr, UINT adapter, D3DDEVTYPE devtype, D3DFORMAT format, D3DFORMAT backformat, BOOL windowed)
+{
+ IDirect3D8 *d3d8 = (IDirect3D8 *)d3dptr->d3dobj;
+ return IDirect3D8_CheckDeviceType(d3d8, adapter, devtype, format, backformat, windowed);
+}
+
+
+static HRESULT d3d_create_device(d3d *d3dptr, UINT adapter, D3DDEVTYPE devtype, HWND focus, DWORD behavior, d3d_present_parameters *params, d3d_device **dev)
+{
+ IDirect3D8 *d3d8 = (IDirect3D8 *)d3dptr->d3dobj;
+ D3DPRESENT_PARAMETERS d3d8params;
+ convert_present_params(params, &d3d8params);
+ return IDirect3D8_CreateDevice(d3d8, adapter, devtype, focus, behavior, &d3d8params, (IDirect3DDevice8 **)dev);
+}
+
+
+static HRESULT d3d_enum_adapter_modes(d3d *d3dptr, UINT adapter, D3DFORMAT format, UINT index, D3DDISPLAYMODE *mode)
+{
+ IDirect3D8 *d3d8 = (IDirect3D8 *)d3dptr->d3dobj;
+ return IDirect3D8_EnumAdapterModes(d3d8, adapter, index, mode);
+}
+
+
+static UINT d3d_get_adapter_count(d3d *d3dptr)
+{
+ IDirect3D8 *d3d8 = (IDirect3D8 *)d3dptr->d3dobj;
+ return IDirect3D8_GetAdapterCount(d3d8);
+}
+
+
+static HRESULT d3d_get_adapter_display_mode(d3d *d3dptr, UINT adapter, D3DDISPLAYMODE *mode)
+{
+ IDirect3D8 *d3d8 = (IDirect3D8 *)d3dptr->d3dobj;
+ return IDirect3D8_GetAdapterDisplayMode(d3d8, adapter, mode);
+}
+
+
+static HRESULT d3d_get_adapter_identifier(d3d *d3dptr, UINT adapter, DWORD flags, d3d_adapter_identifier *identifier)
+{
+ IDirect3D8 *d3d8 = (IDirect3D8 *)d3dptr->d3dobj;
+ D3DADAPTER_IDENTIFIER8 id;
+ HRESULT result = IDirect3D8_GetAdapterIdentifier(d3d8, adapter, flags, &id);
+ memcpy(identifier->Driver, id.Driver, sizeof(identifier->Driver));
+ memcpy(identifier->Description, id.Description, sizeof(identifier->Description));
+ identifier->DriverVersion = id.DriverVersion;
+ identifier->VendorId = id.VendorId;
+ identifier->DeviceId = id.DeviceId;
+ identifier->SubSysId = id.SubSysId;
+ identifier->Revision = id.Revision;
+ identifier->DeviceIdentifier = id.DeviceIdentifier;
+ identifier->WHQLLevel = id.WHQLLevel;
+ return result;
+}
+
+
+static UINT d3d_get_adapter_mode_count(d3d *d3dptr, UINT adapter, D3DFORMAT format)
+{
+ IDirect3D8 *d3d8 = (IDirect3D8 *)d3dptr->d3dobj;
+ return IDirect3D8_GetAdapterModeCount(d3d8, adapter);
+}
+
+
+static HMONITOR d3d_get_adapter_monitor(d3d *d3dptr, UINT adapter)
+{
+ IDirect3D8 *d3d8 = (IDirect3D8 *)d3dptr->d3dobj;
+ return IDirect3D8_GetAdapterMonitor(d3d8, adapter);
+}
+
+
+static HRESULT d3d_get_caps_dword(d3d *d3dptr, UINT adapter, D3DDEVTYPE devtype, d3d_caps_index which, DWORD *value)
+{
+ IDirect3D8 *d3d8 = (IDirect3D8 *)d3dptr->d3dobj;
+ D3DCAPS8 caps;
+ HRESULT result = IDirect3D8_GetDeviceCaps(d3d8, adapter, devtype, &caps);
+ switch (which)
+ {
+ case CAPS_PRESENTATION_INTERVALS: *value = caps.PresentationIntervals; break;
+ case CAPS_CAPS2: *value = caps.DevCaps; break;
+ case CAPS_DEV_CAPS: *value = caps.DevCaps; break;
+ case CAPS_SRCBLEND_CAPS: *value = caps.SrcBlendCaps; break;
+ case CAPS_DSTBLEND_CAPS: *value = caps.DestBlendCaps; break;
+ case CAPS_TEXTURE_CAPS: *value = caps.TextureCaps; break;
+ case CAPS_TEXTURE_FILTER_CAPS: *value = caps.TextureFilterCaps; break;
+ case CAPS_TEXTURE_ADDRESS_CAPS: *value = caps.TextureAddressCaps; break;
+ case CAPS_TEXTURE_OP_CAPS: *value = caps.TextureOpCaps; break;
+ case CAPS_MAX_TEXTURE_ASPECT: *value = caps.MaxTextureAspectRatio; break;
+ case CAPS_MAX_TEXTURE_WIDTH: *value = caps.MaxTextureWidth; break;
+ case CAPS_MAX_TEXTURE_HEIGHT: *value = caps.MaxTextureHeight; break;
+ case CAPS_STRETCH_RECT_FILTER: *value = 0; break;
+ }
+ return result;
+}
+
+
+static ULONG d3d_release(d3d *d3dptr)
+{
+ IDirect3D8 *d3d8 = (IDirect3D8 *)d3dptr->d3dobj;
+ ULONG result = IDirect3D8_Release(d3d8);
+ FreeLibrary(d3dptr->dllhandle);
+ free(d3dptr);
+ return result;
+}
+
+
+static d3d_interface d3d8_interface =
+{
+ d3d_check_device_format,
+ d3d_check_device_type,
+ d3d_create_device,
+ d3d_enum_adapter_modes,
+ d3d_get_adapter_count,
+ d3d_get_adapter_display_mode,
+ d3d_get_adapter_identifier,
+ d3d_get_adapter_mode_count,
+ d3d_get_adapter_monitor,
+ d3d_get_caps_dword,
+ d3d_release
+};
+
+
+
+//============================================================
+// Direct3DDevice interfaces
+//============================================================
+
+static HRESULT device_begin_scene(d3d_device *dev)
+{
+ IDirect3DDevice8 *device = (IDirect3DDevice8 *)dev;
+ return IDirect3DDevice8_BeginScene(device);
+}
+
+
+static HRESULT device_clear(d3d_device *dev, DWORD count, const D3DRECT *rects, DWORD flags, D3DCOLOR color, float z, DWORD stencil)
+{
+ IDirect3DDevice8 *device = (IDirect3DDevice8 *)dev;
+ return IDirect3DDevice8_Clear(device, count, rects, flags, color, z, stencil);
+}
+
+
+static HRESULT device_create_offscreen_plain_surface(d3d_device *dev, UINT width, UINT height, D3DFORMAT format, D3DPOOL pool, d3d_surface **surface)
+{
+ assert(FALSE);
+ return D3D_OK;
+}
+
+
+static HRESULT device_create_texture(d3d_device *dev, UINT width, UINT height, UINT levels, DWORD usage, D3DFORMAT format, D3DPOOL pool, d3d_texture **texture)
+{
+ IDirect3DDevice8 *device = (IDirect3DDevice8 *)dev;
+ return IDirect3DDevice8_CreateTexture(device, width, height, levels, usage, format, pool, (IDirect3DTexture8 **)texture);
+}
+
+
+static HRESULT device_create_vertex_buffer(d3d_device *dev, UINT length, DWORD usage, DWORD fvf, D3DPOOL pool, d3d_vertex_buffer **buf)
+{
+ IDirect3DDevice8 *device = (IDirect3DDevice8 *)dev;
+ return IDirect3DDevice8_CreateVertexBuffer(device, length, usage, fvf, pool, (IDirect3DVertexBuffer8 **)buf);
+}
+
+
+static HRESULT device_draw_primitive(d3d_device *dev, D3DPRIMITIVETYPE type, UINT start, UINT count)
+{
+ IDirect3DDevice8 *device = (IDirect3DDevice8 *)dev;
+ return IDirect3DDevice8_DrawPrimitive(device, type, start, count);
+}
+
+
+static HRESULT device_end_scene(d3d_device *dev)
+{
+ IDirect3DDevice8 *device = (IDirect3DDevice8 *)dev;
+ return IDirect3DDevice8_EndScene(device);
+}
+
+
+static HRESULT device_get_raster_status(d3d_device *dev, D3DRASTER_STATUS *status)
+{
+ IDirect3DDevice8 *device = (IDirect3DDevice8 *)dev;
+ return IDirect3DDevice8_GetRasterStatus(device, status);
+}
+
+
+static HRESULT device_get_render_target(d3d_device *dev, DWORD index, d3d_surface **surface)
+{
+ IDirect3DDevice8 *device = (IDirect3DDevice8 *)dev;
+ assert(index == 0);
+ return IDirect3DDevice8_GetRenderTarget(device, (IDirect3DSurface8 **)surface);
+}
+
+
+static HRESULT device_present(d3d_device *dev, const RECT *source, const RECT *dest, HWND override, RGNDATA *dirty, DWORD flags)
+{
+ IDirect3DDevice8 *device = (IDirect3DDevice8 *)dev;
+ return IDirect3DDevice8_Present(device, source, dest, override, dirty);
+}
+
+
+static ULONG device_release(d3d_device *dev)
+{
+ IDirect3DDevice8 *device = (IDirect3DDevice8 *)dev;
+ return IDirect3DDevice8_Release(device);
+}
+
+
+static HRESULT device_reset(d3d_device *dev, d3d_present_parameters *params)
+{
+ IDirect3DDevice8 *device = (IDirect3DDevice8 *)dev;
+ D3DPRESENT_PARAMETERS d3d8params;
+ convert_present_params(params, &d3d8params);
+ return IDirect3DDevice8_Reset(device, &d3d8params);
+}
+
+
+static void device_set_gamma_ramp(d3d_device *dev, DWORD flags, const D3DGAMMARAMP *ramp)
+{
+ IDirect3DDevice8 *device = (IDirect3DDevice8 *)dev;
+ IDirect3DDevice8_SetGammaRamp(device, flags, ramp);
+}
+
+
+static HRESULT device_set_render_state(d3d_device *dev, D3DRENDERSTATETYPE state, DWORD value)
+{
+ IDirect3DDevice8 *device = (IDirect3DDevice8 *)dev;
+ return IDirect3DDevice8_SetRenderState(device, state, value);
+}
+
+
+static HRESULT device_set_render_target(d3d_device *dev, DWORD index, d3d_surface *surf)
+{
+ IDirect3DDevice8 *device = (IDirect3DDevice8 *)dev;
+ IDirect3DSurface8 *surface = (IDirect3DSurface8 *)surf;
+ assert(index == 0);
+ return IDirect3DDevice8_SetRenderTarget(device, surface, NULL);
+}
+
+
+static HRESULT device_set_stream_source(d3d_device *dev, UINT number, d3d_vertex_buffer *vbuf, UINT stride)
+{
+ IDirect3DDevice8 *device = (IDirect3DDevice8 *)dev;
+ IDirect3DVertexBuffer8 *vertexbuf = (IDirect3DVertexBuffer8 *)vbuf;
+ return IDirect3DDevice8_SetStreamSource(device, number, vertexbuf, stride);
+}
+
+
+static HRESULT device_set_texture(d3d_device *dev, DWORD stage, d3d_texture *tex)
+{
+ IDirect3DDevice8 *device = (IDirect3DDevice8 *)dev;
+ IDirect3DBaseTexture8 *texture = (IDirect3DBaseTexture8 *)tex;
+ return IDirect3DDevice8_SetTexture(device, stage, texture);
+}
+
+
+static HRESULT device_set_texture_stage_state(d3d_device *dev, DWORD stage, D3DTEXTURESTAGESTATETYPE state, DWORD value)
+{
+ IDirect3DDevice8 *device = (IDirect3DDevice8 *)dev;
+ return IDirect3DDevice8_SetTextureStageState(device, stage, state, value);
+}
+
+
+static HRESULT device_set_vertex_shader(d3d_device *dev, D3DFORMAT format)
+{
+ IDirect3DDevice8 *device = (IDirect3DDevice8 *)dev;
+ return IDirect3DDevice8_SetVertexShader(device, format);
+}
+
+
+static HRESULT device_stretch_rect(d3d_device *dev, d3d_surface *source, const RECT *srcrect, d3d_surface *dest, const RECT *dstrect, D3DTEXTUREFILTERTYPE filter)
+{
+ assert(FALSE);
+ return D3D_OK;
+}
+
+
+static HRESULT device_test_cooperative_level(d3d_device *dev)
+{
+ IDirect3DDevice8 *device = (IDirect3DDevice8 *)dev;
+ return IDirect3DDevice8_TestCooperativeLevel(device);
+}
+
+
+static d3d_device_interface d3d8_device_interface =
+{
+ device_begin_scene,
+ device_clear,
+ device_create_offscreen_plain_surface,
+ device_create_texture,
+ device_create_vertex_buffer,
+ device_draw_primitive,
+ device_end_scene,
+ device_get_raster_status,
+ device_get_render_target,
+ device_present,
+ device_release,
+ device_reset,
+ device_set_gamma_ramp,
+ device_set_render_state,
+ device_set_render_target,
+ device_set_stream_source,
+ device_set_texture,
+ device_set_texture_stage_state,
+ device_set_vertex_shader,
+ device_stretch_rect,
+ device_test_cooperative_level
+};
+
+
+
+//============================================================
+// Direct3DSurface interfaces
+//============================================================
+
+static HRESULT surface_lock_rect(d3d_surface *surf, D3DLOCKED_RECT *locked, const RECT *rect, DWORD flags)
+{
+ IDirect3DSurface8 *surface = (IDirect3DSurface8 *)surf;
+ return IDirect3DSurface8_LockRect(surface, locked, rect, flags);
+}
+
+
+static ULONG surface_release(d3d_surface *surf)
+{
+ IDirect3DSurface8 *surface = (IDirect3DSurface8 *)surf;
+ return IDirect3DSurface8_Release(surface);
+}
+
+
+static HRESULT surface_unlock_rect(d3d_surface *surf)
+{
+ IDirect3DSurface8 *surface = (IDirect3DSurface8 *)surf;
+ return IDirect3DSurface8_UnlockRect(surface);
+}
+
+
+static d3d_surface_interface d3d8_surface_interface =
+{
+ surface_lock_rect,
+ surface_release,
+ surface_unlock_rect
+};
+
+
+
+//============================================================
+// Direct3DTexture interfaces
+//============================================================
+
+static HRESULT texture_get_surface_level(d3d_texture *tex, UINT level, d3d_surface **surface)
+{
+ IDirect3DTexture8 *texture = (IDirect3DTexture8 *)tex;
+ return IDirect3DTexture8_GetSurfaceLevel(texture, level, (IDirect3DSurface8 **)surface);
+}
+
+
+static HRESULT texture_lock_rect(d3d_texture *tex, UINT level, D3DLOCKED_RECT *locked, const RECT *rect, DWORD flags)
+{
+ IDirect3DTexture8 *texture = (IDirect3DTexture8 *)tex;
+ return IDirect3DTexture8_LockRect(texture, level, locked, rect, flags);
+}
+
+
+static ULONG texture_release(d3d_texture *tex)
+{
+ IDirect3DTexture8 *texture = (IDirect3DTexture8 *)tex;
+ return IDirect3DTexture8_Release(texture);
+}
+
+
+static HRESULT texture_unlock_rect(d3d_texture *tex, UINT level)
+{
+ IDirect3DTexture8 *texture = (IDirect3DTexture8 *)tex;
+ return IDirect3DTexture8_UnlockRect(texture, level);
+}
+
+
+static d3d_texture_interface d3d8_texture_interface =
+{
+ texture_get_surface_level,
+ texture_lock_rect,
+ texture_release,
+ texture_unlock_rect
+};
+
+
+
+//============================================================
+// Direct3DVertexBuffer interfaces
+//============================================================
+
+static HRESULT vertex_buffer_lock(d3d_vertex_buffer *vbuf, UINT offset, UINT size, VOID **data, DWORD flags)
+{
+ IDirect3DVertexBuffer8 *vertexbuf = (IDirect3DVertexBuffer8 *)vbuf;
+ return IDirect3DVertexBuffer8_Lock(vertexbuf, offset, size, (BYTE **)data, flags);
+}
+
+
+static ULONG vertex_buffer_release(d3d_vertex_buffer *vbuf)
+{
+ IDirect3DVertexBuffer8 *vertexbuf = (IDirect3DVertexBuffer8 *)vbuf;
+ return IDirect3DVertexBuffer8_Release(vertexbuf);
+}
+
+
+static HRESULT vertex_buffer_unlock(d3d_vertex_buffer *vbuf)
+{
+ IDirect3DVertexBuffer8 *vertexbuf = (IDirect3DVertexBuffer8 *)vbuf;
+ return IDirect3DVertexBuffer8_Unlock(vertexbuf);
+}
+
+
+static d3d_vertex_buffer_interface d3d8_vertex_buffer_interface =
+{
+ vertex_buffer_lock,
+ vertex_buffer_release,
+ vertex_buffer_unlock
+};
+
+
+
+//============================================================
+// set_interfaces
+//============================================================
+
+static void set_interfaces(d3d *d3dptr)
+{
+ d3dptr->d3d = d3d8_interface;
+ d3dptr->device = d3d8_device_interface;
+ d3dptr->surface = d3d8_surface_interface;
+ d3dptr->texture = d3d8_texture_interface;
+ d3dptr->vertexbuf = d3d8_vertex_buffer_interface;
+}
diff --git a/src/osd/windows/d3d9intf.c b/src/osd/windows/d3d9intf.c
new file mode 100644
index 00000000000..8884fdeae86
--- /dev/null
+++ b/src/osd/windows/d3d9intf.c
@@ -0,0 +1,590 @@
+//============================================================
+//
+// d3d9intf.c - Direct3D 9 abstraction layer
+//
+// Copyright (c) 1996-2007, Nicola Salmoria and the MAME Team.
+// Visit http://mamedev.org for licensing and usage restrictions.
+//
+//============================================================
+
+// standard windows headers
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#include <d3d9.h>
+
+// MAME headers
+#include "mame.h"
+
+// MAMEOS headers
+#include "d3dintf.h"
+#include "winmain.h"
+
+
+
+//============================================================
+// TYPE DEFINITIONS
+//============================================================
+
+typedef IDirect3D9 *(WINAPI *direct3dcreate9_ptr)(UINT SDKVersion);
+
+
+
+//============================================================
+// PROTOTYPES
+//============================================================
+
+static void set_interfaces(d3d *d3dptr);
+
+
+
+//============================================================
+// INLINES
+//============================================================
+
+INLINE void convert_present_params(const d3d_present_parameters *params, D3DPRESENT_PARAMETERS *d3d9params)
+{
+ memset(d3d9params, 0, sizeof(*d3d9params));
+ d3d9params->BackBufferWidth = params->BackBufferWidth;
+ d3d9params->BackBufferHeight = params->BackBufferHeight;
+ d3d9params->BackBufferFormat = params->BackBufferFormat;
+ d3d9params->BackBufferCount = params->BackBufferCount;
+ d3d9params->MultiSampleType = params->MultiSampleType;
+ d3d9params->MultiSampleQuality = params->MultiSampleQuality;
+ d3d9params->SwapEffect = params->SwapEffect;
+ d3d9params->hDeviceWindow = params->hDeviceWindow;
+ d3d9params->Windowed = params->Windowed;
+ d3d9params->EnableAutoDepthStencil = params->EnableAutoDepthStencil;
+ d3d9params->AutoDepthStencilFormat = params->AutoDepthStencilFormat;
+ d3d9params->Flags = params->Flags;
+ d3d9params->FullScreen_RefreshRateInHz = params->FullScreen_RefreshRateInHz;
+ d3d9params->PresentationInterval = params->PresentationInterval;
+}
+
+
+
+//============================================================
+// drawd3d9_init
+//============================================================
+
+d3d *drawd3d9_init(void)
+{
+ direct3dcreate9_ptr direct3dcreate9;
+ HINSTANCE dllhandle;
+ IDirect3D9 *d3d9;
+ d3d *d3dptr;
+
+ // dynamically grab the create function from d3d9.dll
+ dllhandle = LoadLibrary(TEXT("d3d9.dll"));
+ if (dllhandle == NULL)
+ {
+ mame_printf_verbose("Direct3D: Unable to access d3d9.dll\n");
+ return NULL;
+ }
+
+ // import the create function
+ direct3dcreate9 = (direct3dcreate9_ptr)GetProcAddress(dllhandle, "Direct3DCreate9");
+ if (direct3dcreate9 == NULL)
+ {
+ mame_printf_verbose("Direct3D: Unable to find Direct3DCreate9\n");
+ FreeLibrary(dllhandle);
+ dllhandle = NULL;
+ return NULL;
+ }
+
+ // create our core direct 3d object
+ d3d9 = (*direct3dcreate9)(D3D_SDK_VERSION);
+ if (d3d9 == NULL)
+ {
+ mame_printf_verbose("Direct3D: Error attempting to initialize Direct3D9\n");
+ FreeLibrary(dllhandle);
+ dllhandle = NULL;
+ return NULL;
+ }
+
+ // allocate an object to hold our data
+ d3dptr = malloc_or_die(sizeof(*d3dptr));
+ d3dptr->version = 9;
+ d3dptr->d3dobj = d3d9;
+ d3dptr->dllhandle = dllhandle;
+ set_interfaces(d3dptr);
+
+ mame_printf_verbose("Direct3D: Using Direct3D 9\n");
+ return d3dptr;
+}
+
+
+
+//============================================================
+// Direct3D interfaces
+//============================================================
+
+static HRESULT d3d_check_device_format(d3d *d3dptr, UINT adapter, D3DDEVTYPE devtype, D3DFORMAT adapterformat, DWORD usage, D3DRESOURCETYPE restype, D3DFORMAT format)
+{
+ IDirect3D9 *d3d9 = (IDirect3D9 *)d3dptr->d3dobj;
+ return IDirect3D9_CheckDeviceFormat(d3d9, adapter, devtype, adapterformat, usage, restype, format);
+}
+
+
+static HRESULT d3d_check_device_type(d3d *d3dptr, UINT adapter, D3DDEVTYPE devtype, D3DFORMAT format, D3DFORMAT backformat, BOOL windowed)
+{
+ IDirect3D9 *d3d9 = (IDirect3D9 *)d3dptr->d3dobj;
+ return IDirect3D9_CheckDeviceType(d3d9, adapter, devtype, format, backformat, windowed);
+}
+
+
+static HRESULT d3d_create_device(d3d *d3dptr, UINT adapter, D3DDEVTYPE devtype, HWND focus, DWORD behavior, d3d_present_parameters *params, d3d_device **dev)
+{
+ IDirect3D9 *d3d9 = (IDirect3D9 *)d3dptr->d3dobj;
+ D3DPRESENT_PARAMETERS d3d9params;
+ convert_present_params(params, &d3d9params);
+ return IDirect3D9_CreateDevice(d3d9, adapter, devtype, focus, behavior, &d3d9params, (IDirect3DDevice9 **)dev);
+}
+
+
+static HRESULT d3d_enum_adapter_modes(d3d *d3dptr, UINT adapter, D3DFORMAT format, UINT index, D3DDISPLAYMODE *mode)
+{
+ IDirect3D9 *d3d9 = (IDirect3D9 *)d3dptr->d3dobj;
+ return IDirect3D9_EnumAdapterModes(d3d9, adapter, format, index, mode);
+}
+
+
+static UINT d3d_get_adapter_count(d3d *d3dptr)
+{
+ IDirect3D9 *d3d9 = (IDirect3D9 *)d3dptr->d3dobj;
+ return IDirect3D9_GetAdapterCount(d3d9);
+}
+
+
+static HRESULT d3d_get_adapter_display_mode(d3d *d3dptr, UINT adapter, D3DDISPLAYMODE *mode)
+{
+ IDirect3D9 *d3d9 = (IDirect3D9 *)d3dptr->d3dobj;
+ return IDirect3D9_GetAdapterDisplayMode(d3d9, adapter, mode);
+}
+
+
+static HRESULT d3d_get_adapter_identifier(d3d *d3dptr, UINT adapter, DWORD flags, d3d_adapter_identifier *identifier)
+{
+ IDirect3D9 *d3d9 = (IDirect3D9 *)d3dptr->d3dobj;
+ D3DADAPTER_IDENTIFIER9 id;
+ HRESULT result = IDirect3D9_GetAdapterIdentifier(d3d9, adapter, flags, &id);
+ memcpy(identifier->Driver, id.Driver, sizeof(identifier->Driver));
+ memcpy(identifier->Description, id.Description, sizeof(identifier->Description));
+ identifier->DriverVersion = id.DriverVersion;
+ identifier->VendorId = id.VendorId;
+ identifier->DeviceId = id.DeviceId;
+ identifier->SubSysId = id.SubSysId;
+ identifier->Revision = id.Revision;
+ identifier->DeviceIdentifier = id.DeviceIdentifier;
+ identifier->WHQLLevel = id.WHQLLevel;
+ return result;
+}
+
+
+static UINT d3d_get_adapter_mode_count(d3d *d3dptr, UINT adapter, D3DFORMAT format)
+{
+ IDirect3D9 *d3d9 = (IDirect3D9 *)d3dptr->d3dobj;
+ return IDirect3D9_GetAdapterModeCount(d3d9, adapter, format);
+}
+
+
+static HMONITOR d3d_get_adapter_monitor(d3d *d3dptr, UINT adapter)
+{
+ IDirect3D9 *d3d9 = (IDirect3D9 *)d3dptr->d3dobj;
+ return IDirect3D9_GetAdapterMonitor(d3d9, adapter);
+}
+
+
+static HRESULT d3d_get_caps_dword(d3d *d3dptr, UINT adapter, D3DDEVTYPE devtype, d3d_caps_index which, DWORD *value)
+{
+ IDirect3D9 *d3d9 = (IDirect3D9 *)d3dptr->d3dobj;
+ D3DCAPS9 caps;
+ HRESULT result = IDirect3D9_GetDeviceCaps(d3d9, adapter, devtype, &caps);
+ switch (which)
+ {
+ case CAPS_PRESENTATION_INTERVALS: *value = caps.PresentationIntervals; break;
+ case CAPS_CAPS2: *value = caps.Caps2; break;
+ case CAPS_DEV_CAPS: *value = caps.DevCaps; break;
+ case CAPS_SRCBLEND_CAPS: *value = caps.SrcBlendCaps; break;
+ case CAPS_DSTBLEND_CAPS: *value = caps.DestBlendCaps; break;
+ case CAPS_TEXTURE_CAPS: *value = caps.TextureCaps; break;
+ case CAPS_TEXTURE_FILTER_CAPS: *value = caps.TextureFilterCaps; break;
+ case CAPS_TEXTURE_ADDRESS_CAPS: *value = caps.TextureAddressCaps; break;
+ case CAPS_TEXTURE_OP_CAPS: *value = caps.TextureOpCaps; break;
+ case CAPS_MAX_TEXTURE_ASPECT: *value = caps.MaxTextureAspectRatio; break;
+ case CAPS_MAX_TEXTURE_WIDTH: *value = caps.MaxTextureWidth; break;
+ case CAPS_MAX_TEXTURE_HEIGHT: *value = caps.MaxTextureHeight; break;
+ case CAPS_STRETCH_RECT_FILTER: *value = caps.StretchRectFilterCaps; break;
+ }
+ return result;
+}
+
+
+static ULONG d3d_release(d3d *d3dptr)
+{
+ IDirect3D9 *d3d9 = (IDirect3D9 *)d3dptr->d3dobj;
+ ULONG result = IDirect3D9_Release(d3d9);
+ FreeLibrary(d3dptr->dllhandle);
+ free(d3dptr);
+ return result;
+}
+
+
+static d3d_interface d3d9_interface =
+{
+ d3d_check_device_format,
+ d3d_check_device_type,
+ d3d_create_device,
+ d3d_enum_adapter_modes,
+ d3d_get_adapter_count,
+ d3d_get_adapter_display_mode,
+ d3d_get_adapter_identifier,
+ d3d_get_adapter_mode_count,
+ d3d_get_adapter_monitor,
+ d3d_get_caps_dword,
+ d3d_release
+};
+
+
+
+//============================================================
+// Direct3DDevice interfaces
+//============================================================
+
+static HRESULT device_begin_scene(d3d_device *dev)
+{
+ IDirect3DDevice9 *device = (IDirect3DDevice9 *)dev;
+ return IDirect3DDevice9_BeginScene(device);
+}
+
+
+static HRESULT device_clear(d3d_device *dev, DWORD count, const D3DRECT *rects, DWORD flags, D3DCOLOR color, float z, DWORD stencil)
+{
+ IDirect3DDevice9 *device = (IDirect3DDevice9 *)dev;
+ return IDirect3DDevice9_Clear(device, count, rects, flags, color, z, stencil);
+}
+
+
+static HRESULT device_create_offscreen_plain_surface(d3d_device *dev, UINT width, UINT height, D3DFORMAT format, D3DPOOL pool, d3d_surface **surface)
+{
+ IDirect3DDevice9 *device = (IDirect3DDevice9 *)dev;
+ return IDirect3DDevice9_CreateOffscreenPlainSurface(device, width, height, format, pool, (IDirect3DSurface9 **)surface, NULL);
+}
+
+
+static HRESULT device_create_texture(d3d_device *dev, UINT width, UINT height, UINT levels, DWORD usage, D3DFORMAT format, D3DPOOL pool, d3d_texture **texture)
+{
+ IDirect3DDevice9 *device = (IDirect3DDevice9 *)dev;
+ return IDirect3DDevice9_CreateTexture(device, width, height, levels, usage, format, pool, (IDirect3DTexture9 **)texture, NULL);
+}
+
+
+static HRESULT device_create_vertex_buffer(d3d_device *dev, UINT length, DWORD usage, DWORD fvf, D3DPOOL pool, d3d_vertex_buffer **buf)
+{
+ IDirect3DDevice9 *device = (IDirect3DDevice9 *)dev;
+ return IDirect3DDevice9_CreateVertexBuffer(device, length, usage, fvf, pool, (IDirect3DVertexBuffer9 **)buf, NULL);
+}
+
+
+static HRESULT device_draw_primitive(d3d_device *dev, D3DPRIMITIVETYPE type, UINT start, UINT count)
+{
+ IDirect3DDevice9 *device = (IDirect3DDevice9 *)dev;
+ return IDirect3DDevice9_DrawPrimitive(device, type, start, count);
+}
+
+
+static HRESULT device_end_scene(d3d_device *dev)
+{
+ IDirect3DDevice9 *device = (IDirect3DDevice9 *)dev;
+ return IDirect3DDevice9_EndScene(device);
+}
+
+
+static HRESULT device_get_raster_status(d3d_device *dev, D3DRASTER_STATUS *status)
+{
+ IDirect3DDevice9 *device = (IDirect3DDevice9 *)dev;
+ return IDirect3DDevice9_GetRasterStatus(device, 0, status);
+}
+
+
+static HRESULT device_get_render_target(d3d_device *dev, DWORD index, d3d_surface **surface)
+{
+ IDirect3DDevice9 *device = (IDirect3DDevice9 *)dev;
+ return IDirect3DDevice9_GetRenderTarget(device, index, (IDirect3DSurface9 **)surface);
+}
+
+
+static HRESULT device_present(d3d_device *dev, const RECT *source, const RECT *dest, HWND override, RGNDATA *dirty, DWORD flags)
+{
+ IDirect3DDevice9 *device = (IDirect3DDevice9 *)dev;
+ if (flags != 0)
+ {
+ IDirect3DSwapChain9 *chain;
+ HRESULT result = IDirect3DDevice9_GetSwapChain(device, 0, &chain);
+ if (result == D3D_OK)
+ {
+ result = IDirect3DSwapChain9_Present(chain, source, dest, override, dirty, flags);
+ IDirect3DSwapChain9_Release(chain);
+ return result;
+ }
+ }
+ return IDirect3DDevice9_Present(device, source, dest, override, dirty);
+}
+
+
+static ULONG device_release(d3d_device *dev)
+{
+ IDirect3DDevice9 *device = (IDirect3DDevice9 *)dev;
+ return IDirect3DDevice9_Release(device);
+}
+
+
+static HRESULT device_reset(d3d_device *dev, d3d_present_parameters *params)
+{
+ IDirect3DDevice9 *device = (IDirect3DDevice9 *)dev;
+ D3DPRESENT_PARAMETERS d3d9params;
+ convert_present_params(params, &d3d9params);
+ return IDirect3DDevice9_Reset(device, &d3d9params);
+}
+
+
+static void device_set_gamma_ramp(d3d_device *dev, DWORD flags, const D3DGAMMARAMP *ramp)
+{
+ IDirect3DDevice9 *device = (IDirect3DDevice9 *)dev;
+ IDirect3DDevice9_SetGammaRamp(device, 0, flags, ramp);
+}
+
+
+static HRESULT device_set_render_state(d3d_device *dev, D3DRENDERSTATETYPE state, DWORD value)
+{
+ IDirect3DDevice9 *device = (IDirect3DDevice9 *)dev;
+ return IDirect3DDevice9_SetRenderState(device, state, value);
+}
+
+
+static HRESULT device_set_render_target(d3d_device *dev, DWORD index, d3d_surface *surf)
+{
+ IDirect3DDevice9 *device = (IDirect3DDevice9 *)dev;
+ IDirect3DSurface9 *surface = (IDirect3DSurface9 *)surf;
+ return IDirect3DDevice9_SetRenderTarget(device, index, surface);
+}
+
+
+static HRESULT device_set_stream_source(d3d_device *dev, UINT number, d3d_vertex_buffer *vbuf, UINT stride)
+{
+ IDirect3DDevice9 *device = (IDirect3DDevice9 *)dev;
+ IDirect3DVertexBuffer9 *vertexbuf = (IDirect3DVertexBuffer9 *)vbuf;
+ return IDirect3DDevice9_SetStreamSource(device, number, vertexbuf, 0, stride);
+}
+
+
+static HRESULT device_set_texture(d3d_device *dev, DWORD stage, d3d_texture *tex)
+{
+ IDirect3DDevice9 *device = (IDirect3DDevice9 *)dev;
+ IDirect3DBaseTexture9 *texture = (IDirect3DBaseTexture9 *)tex;
+ return IDirect3DDevice9_SetTexture(device, stage, texture);
+}
+
+
+static HRESULT device_set_texture_stage_state(d3d_device *dev, DWORD stage, D3DTEXTURESTAGESTATETYPE state, DWORD value)
+{
+ IDirect3DDevice9 *device = (IDirect3DDevice9 *)dev;
+
+ // some state which was here got pushed into sampler state in D3D9
+ switch (state)
+ {
+ case D3DTSS_ADDRESSU:
+ return IDirect3DDevice9_SetSamplerState(device, stage, D3DSAMP_ADDRESSU, value);
+ case D3DTSS_ADDRESSV:
+ return IDirect3DDevice9_SetSamplerState(device, stage, D3DSAMP_ADDRESSV, value);
+ case D3DTSS_BORDERCOLOR:
+ return IDirect3DDevice9_SetSamplerState(device, stage, D3DSAMP_BORDERCOLOR, value);
+ case D3DTSS_MAGFILTER:
+ return IDirect3DDevice9_SetSamplerState(device, stage, D3DSAMP_MAGFILTER, value);
+ case D3DTSS_MINFILTER:
+ return IDirect3DDevice9_SetSamplerState(device, stage, D3DSAMP_MINFILTER, value);
+ case D3DTSS_MIPFILTER:
+ return IDirect3DDevice9_SetSamplerState(device, stage, D3DSAMP_MIPFILTER, value);
+ case D3DTSS_MIPMAPLODBIAS:
+ return IDirect3DDevice9_SetSamplerState(device, stage, D3DSAMP_MIPMAPLODBIAS, value);
+ case D3DTSS_MAXMIPLEVEL:
+ return IDirect3DDevice9_SetSamplerState(device, stage, D3DSAMP_MAXMIPLEVEL, value);
+ case D3DTSS_MAXANISOTROPY:
+ return IDirect3DDevice9_SetSamplerState(device, stage, D3DSAMP_MAXANISOTROPY, value);
+ default:
+ return IDirect3DDevice9_SetTextureStageState(device, stage, state, value);
+ }
+}
+
+
+static HRESULT device_set_vertex_shader(d3d_device *dev, D3DFORMAT format)
+{
+ IDirect3DDevice9 *device = (IDirect3DDevice9 *)dev;
+ return IDirect3DDevice9_SetFVF(device, format);
+}
+
+
+static HRESULT device_stretch_rect(d3d_device *dev, d3d_surface *source, const RECT *srcrect, d3d_surface *dest, const RECT *dstrect, D3DTEXTUREFILTERTYPE filter)
+{
+ IDirect3DDevice9 *device = (IDirect3DDevice9 *)dev;
+ IDirect3DSurface9 *ssurface = (IDirect3DSurface9 *)source;
+ IDirect3DSurface9 *dsurface = (IDirect3DSurface9 *)dest;
+ return IDirect3DDevice9_StretchRect(device, ssurface, srcrect, dsurface, dstrect, filter);
+}
+
+
+static HRESULT device_test_cooperative_level(d3d_device *dev)
+{
+ IDirect3DDevice9 *device = (IDirect3DDevice9 *)dev;
+ return IDirect3DDevice9_TestCooperativeLevel(device);
+}
+
+
+static d3d_device_interface d3d9_device_interface =
+{
+ device_begin_scene,
+ device_clear,
+ device_create_offscreen_plain_surface,
+ device_create_texture,
+ device_create_vertex_buffer,
+ device_draw_primitive,
+ device_end_scene,
+ device_get_raster_status,
+ device_get_render_target,
+ device_present,
+ device_release,
+ device_reset,
+ device_set_gamma_ramp,
+ device_set_render_state,
+ device_set_render_target,
+ device_set_stream_source,
+ device_set_texture,
+ device_set_texture_stage_state,
+ device_set_vertex_shader,
+ device_stretch_rect,
+ device_test_cooperative_level
+};
+
+
+
+//============================================================
+// Direct3DSurface interfaces
+//============================================================
+
+static HRESULT surface_lock_rect(d3d_surface *surf, D3DLOCKED_RECT *locked, const RECT *rect, DWORD flags)
+{
+ IDirect3DSurface9 *surface = (IDirect3DSurface9 *)surf;
+ return IDirect3DSurface9_LockRect(surface, locked, rect, flags);
+}
+
+
+static ULONG surface_release(d3d_surface *surf)
+{
+ IDirect3DSurface9 *surface = (IDirect3DSurface9 *)surf;
+ return IDirect3DSurface9_Release(surface);
+}
+
+
+static HRESULT surface_unlock_rect(d3d_surface *surf)
+{
+ IDirect3DSurface9 *surface = (IDirect3DSurface9 *)surf;
+ return IDirect3DSurface9_UnlockRect(surface);
+}
+
+
+static d3d_surface_interface d3d9_surface_interface =
+{
+ surface_lock_rect,
+ surface_release,
+ surface_unlock_rect
+};
+
+
+
+//============================================================
+// Direct3DTexture interfaces
+//============================================================
+
+static HRESULT texture_get_surface_level(d3d_texture *tex, UINT level, d3d_surface **surface)
+{
+ IDirect3DTexture9 *texture = (IDirect3DTexture9 *)tex;
+ return IDirect3DTexture9_GetSurfaceLevel(texture, level, (IDirect3DSurface9 **)surface);
+}
+
+
+static HRESULT texture_lock_rect(d3d_texture *tex, UINT level, D3DLOCKED_RECT *locked, const RECT *rect, DWORD flags)
+{
+ IDirect3DTexture9 *texture = (IDirect3DTexture9 *)tex;
+ return IDirect3DTexture9_LockRect(texture, level, locked, rect, flags);
+}
+
+
+static ULONG texture_release(d3d_texture *tex)
+{
+ IDirect3DTexture9 *texture = (IDirect3DTexture9 *)tex;
+ return IDirect3DTexture9_Release(texture);
+}
+
+
+static HRESULT texture_unlock_rect(d3d_texture *tex, UINT level)
+{
+ IDirect3DTexture9 *texture = (IDirect3DTexture9 *)tex;
+ return IDirect3DTexture9_UnlockRect(texture, level);
+}
+
+
+static d3d_texture_interface d3d9_texture_interface =
+{
+ texture_get_surface_level,
+ texture_lock_rect,
+ texture_release,
+ texture_unlock_rect
+};
+
+
+
+//============================================================
+// Direct3DVertexBuffer interfaces
+//============================================================
+
+static HRESULT vertex_buffer_lock(d3d_vertex_buffer *vbuf, UINT offset, UINT size, VOID **data, DWORD flags)
+{
+ IDirect3DVertexBuffer9 *vertexbuf = (IDirect3DVertexBuffer9 *)vbuf;
+ return IDirect3DVertexBuffer9_Lock(vertexbuf, offset, size, data, flags);
+}
+
+
+static ULONG vertex_buffer_release(d3d_vertex_buffer *vbuf)
+{
+ IDirect3DVertexBuffer9 *vertexbuf = (IDirect3DVertexBuffer9 *)vbuf;
+ return IDirect3DVertexBuffer9_Release(vertexbuf);
+}
+
+
+static HRESULT vertex_buffer_unlock(d3d_vertex_buffer *vbuf)
+{
+ IDirect3DVertexBuffer9 *vertexbuf = (IDirect3DVertexBuffer9 *)vbuf;
+ return IDirect3DVertexBuffer9_Unlock(vertexbuf);
+}
+
+
+static d3d_vertex_buffer_interface d3d9_vertex_buffer_interface =
+{
+ vertex_buffer_lock,
+ vertex_buffer_release,
+ vertex_buffer_unlock
+};
+
+
+
+//============================================================
+// set_interfaces
+//============================================================
+
+static void set_interfaces(d3d *d3dptr)
+{
+ d3dptr->d3d = d3d9_interface;
+ d3dptr->device = d3d9_device_interface;
+ d3dptr->surface = d3d9_surface_interface;
+ d3dptr->texture = d3d9_texture_interface;
+ d3dptr->vertexbuf = d3d9_vertex_buffer_interface;
+}
diff --git a/src/osd/windows/d3dintf.h b/src/osd/windows/d3dintf.h
new file mode 100644
index 00000000000..4c3fee4099f
--- /dev/null
+++ b/src/osd/windows/d3dintf.h
@@ -0,0 +1,241 @@
+//============================================================
+//
+// drawd3di.h - Direct3D 8/9 interface abstractions
+//
+// Copyright (c) 1996-2007, Nicola Salmoria and the MAME Team.
+// Visit http://mamedev.org for licensing and usage restrictions.
+//
+//============================================================
+
+#ifndef __WIN_DRAWD3DI__
+#define __WIN_DRAWD3DI__
+
+
+//============================================================
+// CONSTANTS
+//============================================================
+
+#ifndef D3DCAPS2_DYNAMICTEXTURES
+#define D3DCAPS2_DYNAMICTEXTURES 0x20000000L
+#endif
+
+#ifndef D3DPRESENT_DONOTWAIT
+#define D3DPRESENT_DONOTWAIT 0x00000001L
+#endif
+
+
+#if (DIRECT3D_VERSION >= 0x0900)
+// the following used to be TEXTURESTAGESTATES but are now SAMPLERSTATES
+enum
+{
+ D3DTSS_ADDRESSU = 13,
+ D3DTSS_ADDRESSV = 14,
+ D3DTSS_BORDERCOLOR = 15,
+ D3DTSS_MAGFILTER = 16,
+ D3DTSS_MINFILTER = 17,
+ D3DTSS_MIPFILTER = 18,
+ D3DTSS_MIPMAPLODBIAS = 19,
+ D3DTSS_MAXMIPLEVEL = 20,
+ D3DTSS_MAXANISOTROPY = 21
+};
+#endif
+
+
+//============================================================
+// TYPE DEFINITIONS
+//============================================================
+
+typedef struct _d3d d3d;
+typedef struct _d3d_device d3d_device;
+typedef struct _d3d_surface d3d_surface;
+typedef struct _d3d_texture d3d_texture;
+typedef struct _d3d_vertex_buffer d3d_vertex_buffer;
+
+
+//============================================================
+// Abstracted presentation parameters
+//============================================================
+
+typedef struct _d3d_present_parameters d3d_present_parameters;
+struct _d3d_present_parameters
+{
+ UINT BackBufferWidth;
+ UINT BackBufferHeight;
+ D3DFORMAT BackBufferFormat;
+ UINT BackBufferCount;
+ D3DMULTISAMPLE_TYPE MultiSampleType;
+ DWORD MultiSampleQuality;
+ D3DSWAPEFFECT SwapEffect;
+ HWND hDeviceWindow;
+ BOOL Windowed;
+ BOOL EnableAutoDepthStencil;
+ D3DFORMAT AutoDepthStencilFormat;
+ DWORD Flags;
+ UINT FullScreen_RefreshRateInHz;
+ UINT PresentationInterval;
+};
+
+
+//============================================================
+// Abstracted device identifier
+//============================================================
+
+typedef struct _d3d_adapter_identifier d3d_adapter_identifier;
+struct _d3d_adapter_identifier
+{
+ char Driver[512];
+ char Description[512];
+ LARGE_INTEGER DriverVersion;
+ DWORD VendorId;
+ DWORD DeviceId;
+ DWORD SubSysId;
+ DWORD Revision;
+ GUID DeviceIdentifier;
+ DWORD WHQLLevel;
+};
+
+
+//============================================================
+// Caps enumeration
+//============================================================
+
+enum _d3d_caps_index
+{
+ CAPS_PRESENTATION_INTERVALS,
+ CAPS_CAPS2,
+ CAPS_DEV_CAPS,
+ CAPS_SRCBLEND_CAPS,
+ CAPS_DSTBLEND_CAPS,
+ CAPS_TEXTURE_CAPS,
+ CAPS_TEXTURE_FILTER_CAPS,
+ CAPS_TEXTURE_ADDRESS_CAPS,
+ CAPS_TEXTURE_OP_CAPS,
+ CAPS_MAX_TEXTURE_ASPECT,
+ CAPS_MAX_TEXTURE_WIDTH,
+ CAPS_MAX_TEXTURE_HEIGHT,
+ CAPS_STRETCH_RECT_FILTER
+};
+typedef enum _d3d_caps_index d3d_caps_index;
+
+
+//============================================================
+// Direct3D interfaces
+//============================================================
+
+typedef struct _d3d_interface d3d_interface;
+struct _d3d_interface
+{
+ HRESULT (*check_device_format)(d3d *d3dptr, UINT adapter, D3DDEVTYPE devtype, D3DFORMAT adapterformat, DWORD usage, D3DRESOURCETYPE restype, D3DFORMAT format);
+ HRESULT (*check_device_type)(d3d *d3dptr, UINT adapter, D3DDEVTYPE devtype, D3DFORMAT format, D3DFORMAT backformat, BOOL windowed);
+ HRESULT (*create_device)(d3d *d3dptr, UINT adapter, D3DDEVTYPE devtype, HWND focus, DWORD behavior, d3d_present_parameters *params, d3d_device **dev);
+ HRESULT (*enum_adapter_modes)(d3d *d3dptr, UINT adapter, D3DFORMAT format, UINT index, D3DDISPLAYMODE *mode);
+ UINT (*get_adapter_count)(d3d *d3dptr);
+ HRESULT (*get_adapter_display_mode)(d3d *d3dptr, UINT adapter, D3DDISPLAYMODE *mode);
+ HRESULT (*get_adapter_identifier)(d3d *d3dptr, UINT adapter, DWORD flags, d3d_adapter_identifier *identifier);
+ UINT (*get_adapter_mode_count)(d3d *d3dptr, UINT adapter, D3DFORMAT format);
+ HMONITOR (*get_adapter_monitor)(d3d *d3dptr, UINT adapter);
+ HRESULT (*get_caps_dword)(d3d *d3dptr, UINT adapter, D3DDEVTYPE devtype, d3d_caps_index which, DWORD *value);
+ ULONG (*release)(d3d *d3dptr);
+};
+
+
+//============================================================
+// Direct3DDevice interfaces
+//============================================================
+
+typedef struct _d3d_device_interface d3d_device_interface;
+struct _d3d_device_interface
+{
+ HRESULT (*begin_scene)(d3d_device *dev);
+ HRESULT (*clear)(d3d_device *dev, DWORD count, const D3DRECT *rects, DWORD flags, D3DCOLOR color, float z, DWORD stencil);
+ HRESULT (*create_offscreen_plain_surface)(d3d_device *dev, UINT width, UINT height, D3DFORMAT format, D3DPOOL pool, d3d_surface **surface);
+ HRESULT (*create_texture)(d3d_device *dev, UINT width, UINT height, UINT levels, DWORD usage, D3DFORMAT format, D3DPOOL pool, d3d_texture **texture);
+ HRESULT (*create_vertex_buffer)(d3d_device *dev, UINT length, DWORD usage, DWORD fvf, D3DPOOL pool, d3d_vertex_buffer **buf);
+ HRESULT (*draw_primitive)(d3d_device *dev, D3DPRIMITIVETYPE type, UINT start, UINT count);
+ HRESULT (*end_scene)(d3d_device *dev);
+ HRESULT (*get_raster_status)(d3d_device *dev, D3DRASTER_STATUS *status);
+ HRESULT (*get_render_target)(d3d_device *dev, DWORD index, d3d_surface **surface);
+ HRESULT (*present)(d3d_device *dev, const RECT *source, const RECT *dest, HWND override, RGNDATA *dirty, DWORD flags);
+ ULONG (*release)(d3d_device *dev);
+ HRESULT (*reset)(d3d_device *dev, d3d_present_parameters *params);
+ void (*set_gamma_ramp)(d3d_device *dev, DWORD flags, const D3DGAMMARAMP *ramp);
+ HRESULT (*set_render_state)(d3d_device *dev, D3DRENDERSTATETYPE state, DWORD value);
+ HRESULT (*set_render_target)(d3d_device *dev, DWORD index, d3d_surface *surf);
+ HRESULT (*set_stream_source)(d3d_device *dev, UINT number, d3d_vertex_buffer *vbuf, UINT stride);
+ HRESULT (*set_texture)(d3d_device *dev, DWORD stage, d3d_texture *tex);
+ HRESULT (*set_texture_stage_state)(d3d_device *dev, DWORD stage, D3DTEXTURESTAGESTATETYPE state, DWORD value);
+ HRESULT (*set_vertex_shader)(d3d_device *dev, D3DFORMAT format);
+ HRESULT (*stretch_rect)(d3d_device *dev, d3d_surface *source, const RECT *srcrect, d3d_surface *dest, const RECT *dstrect, D3DTEXTUREFILTERTYPE filter);
+ HRESULT (*test_cooperative_level)(d3d_device *dev);
+};
+
+
+//============================================================
+// Direct3DSurface interfaces
+//============================================================
+
+typedef struct _d3d_surface_interface d3d_surface_interface;
+struct _d3d_surface_interface
+{
+ HRESULT (*lock_rect)(d3d_surface *surf, D3DLOCKED_RECT *locked, const RECT *rect, DWORD flags);
+ ULONG (*release)(d3d_surface *tex);
+ HRESULT (*unlock_rect)(d3d_surface *surf);
+};
+
+
+//============================================================
+// Direct3DTexture interfaces
+//============================================================
+
+typedef struct _d3d_texture_interface d3d_texture_interface;
+struct _d3d_texture_interface
+{
+ HRESULT (*get_surface_level)(d3d_texture *tex, UINT level, d3d_surface **surface);
+ HRESULT (*lock_rect)(d3d_texture *tex, UINT level, D3DLOCKED_RECT *locked, const RECT *rect, DWORD flags);
+ ULONG (*release)(d3d_texture *tex);
+ HRESULT (*unlock_rect)(d3d_texture *tex, UINT level);
+};
+
+
+//============================================================
+// Direct3DVertexBuffer interfaces
+//============================================================
+
+typedef struct _d3d_vertex_buffer_interface d3d_vertex_buffer_interface;
+struct _d3d_vertex_buffer_interface
+{
+ HRESULT (*lock)(d3d_vertex_buffer *vbuf, UINT offset, UINT size, VOID **data, DWORD flags);
+ ULONG (*release)(d3d_vertex_buffer *vbuf);
+ HRESULT (*unlock)(d3d_vertex_buffer *vbuf);
+};
+
+
+//============================================================
+// Core D3D object
+//============================================================
+
+struct _d3d
+{
+ // internal objects
+ int version;
+ void * d3dobj;
+ HINSTANCE dllhandle;
+
+ // interface pointers
+ d3d_interface d3d;
+ d3d_device_interface device;
+ d3d_surface_interface surface;
+ d3d_texture_interface texture;
+ d3d_vertex_buffer_interface vertexbuf;
+};
+
+
+//============================================================
+// PROTOTYPES
+//============================================================
+
+d3d *drawd3d8_init(void);
+d3d *drawd3d9_init(void);
+
+
+#endif
diff --git a/src/osd/windows/debugwin.c b/src/osd/windows/debugwin.c
new file mode 100644
index 00000000000..3a1a3c7a9f8
--- /dev/null
+++ b/src/osd/windows/debugwin.c
@@ -0,0 +1,3025 @@
+//============================================================
+//
+// debugwin.c - Win32 debug window handling
+//
+// Copyright (c) 1996-2007, Nicola Salmoria and the MAME Team.
+// Visit http://mamedev.org for licensing and usage restrictions.
+//
+//============================================================
+
+// standard windows headers
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#include <windowsx.h>
+#include <tchar.h>
+#ifdef _MSC_VER
+#include <zmouse.h>
+#endif
+
+// MAME headers
+#include "driver.h"
+#include "debug/debugvw.h"
+#include "debug/debugcon.h"
+#include "debug/debugcpu.h"
+
+// MAMEOS headers
+#include "debugwin.h"
+#include "window.h"
+#include "video.h"
+#include "config.h"
+#include "strconv.h"
+#include "winutf8.h"
+
+
+
+//============================================================
+// PARAMETERS
+//============================================================
+
+#define MAX_VIEWS 4
+#define EDGE_WIDTH 3
+#define MAX_EDIT_STRING 256
+#define HISTORY_LENGTH 20
+#define MAX_OTHER_WND 4
+
+// debugger window styles
+#define DEBUG_WINDOW_STYLE (WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN) & (~WS_MINIMIZEBOX & ~WS_MAXIMIZEBOX)
+#define DEBUG_WINDOW_STYLE_EX 0
+
+// debugger view styles
+#define DEBUG_VIEW_STYLE WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN
+#define DEBUG_VIEW_STYLE_EX 0
+
+// edit box styles
+#define EDIT_BOX_STYLE WS_CHILD | WS_VISIBLE | ES_AUTOHSCROLL
+#define EDIT_BOX_STYLE_EX 0
+
+// combo box styles
+#define COMBO_BOX_STYLE WS_CHILD | WS_VISIBLE | CBS_DROPDOWNLIST
+#define COMBO_BOX_STYLE_EX 0
+
+// horizontal scroll bar styles
+#define HSCROLL_STYLE WS_CHILD | WS_VISIBLE | SBS_HORZ
+#define HSCROLL_STYLE_EX 0
+
+// vertical scroll bar styles
+#define VSCROLL_STYLE WS_CHILD | WS_VISIBLE | SBS_VERT
+#define VSCROLL_STYLE_EX 0
+
+
+enum
+{
+ ID_NEW_MEMORY_WND = 1,
+ ID_NEW_DISASM_WND,
+ ID_NEW_LOG_WND,
+ ID_RUN,
+ ID_RUN_AND_HIDE,
+ ID_RUN_VBLANK,
+ ID_RUN_IRQ,
+ ID_NEXT_CPU,
+ ID_STEP,
+ ID_STEP_OVER,
+ ID_STEP_OUT,
+ ID_HARD_RESET,
+ ID_SOFT_RESET,
+ ID_EXIT,
+
+ ID_1_BYTE_CHUNKS,
+ ID_2_BYTE_CHUNKS,
+ ID_4_BYTE_CHUNKS,
+ ID_8_BYTE_CHUNKS,
+ ID_LOGICAL_ADDRESSES,
+ ID_PHYSICAL_ADDRESSES,
+ ID_REVERSE_VIEW,
+ ID_INCREASE_MEM_WIDTH,
+ ID_DECREASE_MEM_WIDTH,
+
+ ID_SHOW_RAW,
+ ID_SHOW_ENCRYPTED,
+ ID_SHOW_COMMENTS,
+ ID_RUN_TO_CURSOR,
+ ID_TOGGLE_BREAKPOINT
+};
+
+
+
+//============================================================
+// TYPES
+//============================================================
+
+typedef struct _debugview_info debugview_info;
+typedef struct _debugwin_info debugwin_info;
+
+
+struct _debugview_info
+{
+ debugwin_info * owner;
+ debug_view * view;
+ HWND wnd;
+ HWND hscroll;
+ HWND vscroll;
+ UINT8 is_textbuf;
+};
+
+
+struct _debugwin_info
+{
+ debugwin_info * next;
+ HWND wnd;
+ HWND focuswnd;
+ WNDPROC handler;
+
+ UINT32 minwidth, maxwidth;
+ UINT32 minheight, maxheight;
+ void (*recompute_children)(debugwin_info *);
+
+ int (*handle_command)(debugwin_info *, WPARAM, LPARAM);
+ int (*handle_key)(debugwin_info *, WPARAM, LPARAM);
+ UINT16 ignore_char_lparam;
+
+ debugview_info view[MAX_VIEWS];
+
+ HWND editwnd;
+ char edit_defstr[256];
+ void (*process_string)(debugwin_info *, const char *);
+ WNDPROC original_editproc;
+ TCHAR history[HISTORY_LENGTH][MAX_EDIT_STRING];
+ int history_count;
+ int last_history;
+
+ HWND otherwnd[MAX_OTHER_WND];
+};
+
+
+typedef struct _memorycombo_item memorycombo_item;
+struct _memorycombo_item
+{
+ memorycombo_item * next;
+ char name[256];
+ UINT8 cpunum;
+ UINT8 spacenum;
+ void * base;
+ UINT32 length;
+ UINT8 offset_xor;
+ UINT8 little_endian;
+ UINT8 prefsize;
+};
+
+
+//============================================================
+// GLOBAL VARIABLES
+//============================================================
+
+
+
+//============================================================
+// LOCAL VARIABLES
+//============================================================
+
+static debugwin_info *window_list;
+static debugwin_info *main_console;
+
+static memorycombo_item *memorycombo;
+
+static UINT8 debugger_active_countdown;
+static UINT8 waiting_for_debugger;
+
+static HFONT debug_font;
+static UINT32 debug_font_height;
+static UINT32 debug_font_width;
+static UINT32 debug_font_ascent;
+
+static UINT32 hscroll_height;
+static UINT32 vscroll_width;
+
+static DWORD last_debugger_update;
+
+static UINT8 temporarily_fake_that_we_are_not_visible;
+
+
+
+//============================================================
+// PROTOTYPES
+//============================================================
+
+static debugwin_info *debug_window_create(LPCSTR title, WNDPROC handler);
+static void debug_window_free(debugwin_info *info);
+static LRESULT CALLBACK debug_window_proc(HWND wnd, UINT message, WPARAM wparam, LPARAM lparam);
+
+static void debug_view_draw_contents(debugview_info *view, HDC dc);
+static debugview_info *debug_view_find(debug_view *view);
+static LRESULT CALLBACK debug_view_proc(HWND wnd, UINT message, WPARAM wparam, LPARAM lparam);
+static void debug_view_update(debug_view *view);
+static int debug_view_create(debugwin_info *info, int which, int type);
+
+static LRESULT CALLBACK debug_edit_proc(HWND wnd, UINT message, WPARAM wparam, LPARAM lparam);
+
+//static void generic_create_window(int type);
+static void generic_recompute_children(debugwin_info *info);
+
+static void memory_create_window(void);
+static void memory_recompute_children(debugwin_info *info);
+static void memory_process_string(debugwin_info *info, const char *string);
+static void memory_update_checkmarks(debugwin_info *info);
+static int memory_handle_command(debugwin_info *info, WPARAM wparam, LPARAM lparam);
+static int memory_handle_key(debugwin_info *info, WPARAM wparam, LPARAM lparam);
+
+static void disasm_create_window(void);
+static void disasm_recompute_children(debugwin_info *info);
+static void disasm_process_string(debugwin_info *info, const char *string);
+static void disasm_update_checkmarks(debugwin_info *info);
+static int disasm_handle_command(debugwin_info *info, WPARAM wparam, LPARAM lparam);
+static int disasm_handle_key(debugwin_info *info, WPARAM wparam, LPARAM lparam);
+static void disasm_update_caption(HWND wnd);
+
+static void console_create_window(void);
+static void console_recompute_children(debugwin_info *info);
+static void console_process_string(debugwin_info *info, const char *string);
+static void console_set_cpunum(int cpunum);
+
+static HMENU create_standard_menubar(void);
+static int global_handle_command(debugwin_info *info, WPARAM wparam, LPARAM lparam);
+static int global_handle_key(debugwin_info *info, WPARAM wparam, LPARAM lparam);
+static void smart_set_window_bounds(HWND wnd, HWND parent, RECT *bounds);
+static void smart_show_window(HWND wnd, BOOL show);
+static void smart_show_all(BOOL show);
+
+
+
+//============================================================
+// osd_wait_for_debugger
+//============================================================
+
+void osd_wait_for_debugger(void)
+{
+ MSG message;
+
+ // create a console window
+ if (main_console == NULL)
+ console_create_window();
+
+ // update the views in the console to reflect the current CPU
+ if (main_console != NULL)
+ console_set_cpunum(cpu_getactivecpu());
+
+ // make sure the debug windows are visible
+ waiting_for_debugger = TRUE;
+ smart_show_all(TRUE);
+
+ // get and process messages
+ GetMessage(&message, NULL, 0, 0);
+ last_debugger_update = GetTickCount();
+
+ switch (message.message)
+ {
+ // check for F10 -- we need to capture that ourselves
+ case WM_SYSKEYDOWN:
+ case WM_SYSKEYUP:
+ if (message.wParam == VK_F10)
+ SendMessage(GetFocus(), (message.message == WM_SYSKEYDOWN) ? WM_KEYDOWN : WM_KEYUP, message.wParam, message.lParam);
+ break;
+
+ // process everything else
+ default:
+ winwindow_dispatch_message(&message);
+ break;
+ }
+
+ // mark the debugger as active
+ debugger_active_countdown = 2;
+ waiting_for_debugger = FALSE;
+}
+
+
+
+//============================================================
+// debugwin_init_windows
+//============================================================
+
+void debugwin_init_windows(void)
+{
+ static int class_registered;
+
+ // register the window classes
+ if (!class_registered)
+ {
+ WNDCLASS wc = { 0 };
+
+ // initialize the description of the window class
+ wc.lpszClassName = TEXT("MAMEDebugWindow");
+ wc.hInstance = GetModuleHandle(NULL);
+ wc.lpfnWndProc = debug_window_proc;
+ wc.hCursor = LoadCursor(NULL, IDC_ARROW);
+ wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
+ wc.lpszMenuName = NULL;
+ wc.hbrBackground = NULL;
+ wc.style = 0;
+ wc.cbClsExtra = 0;
+ wc.cbWndExtra = 0;
+
+ // register the class; fail if we can't
+ if (!RegisterClass(&wc))
+ fatalerror("Unable to register debug window class");
+
+ // initialize the description of the view class
+ wc.lpszClassName = TEXT("MAMEDebugView");
+ wc.lpfnWndProc = debug_view_proc;
+
+ // register the class; fail if we can't
+ if (!RegisterClass(&wc))
+ fatalerror("Unable to register debug view class");
+
+ class_registered = TRUE;
+ }
+
+ // create the font
+ if (debug_font == NULL)
+ {
+ // create a temporary DC
+ HDC temp_dc = GetDC(NULL);
+ TEXTMETRIC metrics;
+ HGDIOBJ old_font;
+
+ if (temp_dc != NULL)
+ {
+ // create a standard Lucida Console 8 font
+ debug_font = CreateFont(-MulDiv(8, GetDeviceCaps(temp_dc, LOGPIXELSY), 72), 0, 0, 0, FW_MEDIUM, FALSE, FALSE, FALSE,
+ ANSI_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, FF_DONTCARE, TEXT("Lucida Console"));
+ if (debug_font == NULL)
+ fatalerror("Unable to create debug font");
+
+ // get the metrics
+ old_font = SelectObject(temp_dc, debug_font);
+ if (GetTextMetrics(temp_dc, &metrics))
+ {
+ debug_font_width = metrics.tmMaxCharWidth;
+ debug_font_height = metrics.tmHeight;
+ debug_font_ascent = metrics.tmAscent + metrics.tmExternalLeading;
+ }
+ SelectObject(temp_dc, old_font);
+ ReleaseDC(NULL, temp_dc);
+ }
+ }
+
+ // get other metrics
+ hscroll_height = GetSystemMetrics(SM_CYHSCROLL);
+ vscroll_width = GetSystemMetrics(SM_CXVSCROLL);
+}
+
+
+
+//============================================================
+// debugwin_destroy_windows
+//============================================================
+
+void debugwin_destroy_windows(void)
+{
+ // loop over windows and free them
+ while (window_list)
+ DestroyWindow(window_list->wnd);
+
+ // free the combobox info
+ while (memorycombo)
+ {
+ void *temp = memorycombo;
+ memorycombo = memorycombo->next;
+ free(temp);
+ }
+
+ main_console = NULL;
+}
+
+
+
+//============================================================
+// debugwin_show
+//============================================================
+
+void debugwin_show(int type)
+{
+ debugwin_info *info;
+
+ // loop over windows and show/hide them
+ for (info = window_list; info; info = info->next)
+ ShowWindow(info->wnd, type);
+}
+
+
+
+//============================================================
+// debugwin_update_during_game
+//============================================================
+
+void debugwin_update_during_game(void)
+{
+ int execution_state = debug_get_execution_state();
+
+ // if we're running live, do some checks
+ if (execution_state != EXECUTION_STATE_STOPPED)
+ {
+ // see if the interrupt key is pressed and break if it is
+ temporarily_fake_that_we_are_not_visible = TRUE;
+ if (input_ui_pressed(IPT_UI_DEBUG_BREAK))
+ {
+ debugwin_info *info;
+ HWND focuswnd = GetFocus();
+
+ debug_halt_on_next_instruction();
+ debug_console_printf("User-initiated break\n");
+
+ // if we were focused on some window's edit box, reset it to default
+ for (info = window_list; info; info = info->next)
+ if (focuswnd == info->editwnd)
+ {
+ SendMessage(focuswnd, WM_SETTEXT, (WPARAM)0, (LPARAM)info->edit_defstr);
+ SendMessage(focuswnd, EM_SETSEL, (WPARAM)0, (LPARAM)-1);
+ }
+ }
+ temporarily_fake_that_we_are_not_visible = FALSE;
+ }
+}
+
+
+
+//============================================================
+// debugwin_is_debugger_visible
+//============================================================
+
+int debugwin_is_debugger_visible(void)
+{
+ debugwin_info *info;
+
+ // a bit of hackiness to allow us to check key sequences even if we are visible
+ if (temporarily_fake_that_we_are_not_visible)
+ return 0;
+
+ // if any one of our windows is visible, return true
+ for (info = window_list; info; info = info->next)
+ if (IsWindowVisible(info->wnd))
+ return 1;
+ return 0;
+}
+
+
+
+//============================================================
+// debug_window_create
+//============================================================
+
+static debugwin_info *debug_window_create(LPCSTR title, WNDPROC handler)
+{
+ debugwin_info *info = NULL;
+ RECT work_bounds;
+
+ // allocate memory
+ info = malloc_or_die(sizeof(*info));
+ memset(info, 0, sizeof(*info));
+
+ // create the window
+ info->handler = handler;
+ info->wnd = win_create_window_ex_utf8(DEBUG_WINDOW_STYLE_EX, "MAMEDebugWindow", title, DEBUG_WINDOW_STYLE,
+ 0, 0, 100, 100, win_window_list->hwnd, create_standard_menubar(), GetModuleHandle(NULL), info);
+ if (info->wnd == NULL)
+ goto cleanup;
+
+ // fill in some defaults
+ SystemParametersInfo(SPI_GETWORKAREA, 0, &work_bounds, 0);
+ info->minwidth = 200;
+ info->minheight = 200;
+ info->maxwidth = work_bounds.right - work_bounds.left;
+ info->maxheight = work_bounds.bottom - work_bounds.top;
+
+ // set the default handlers
+ info->handle_command = global_handle_command;
+ info->handle_key = global_handle_key;
+ strcpy(info->edit_defstr, "");
+
+ // hook us in
+ info->next = window_list;
+ window_list = info;
+
+ return info;
+
+cleanup:
+ if (info->wnd != NULL)
+ DestroyWindow(info->wnd);
+ free(info);
+ return NULL;
+}
+
+
+
+//============================================================
+// debug_window_free
+//============================================================
+
+static void debug_window_free(debugwin_info *info)
+{
+ debugwin_info *prev, *curr;
+ int viewnum;
+
+ // first unlink us from the list
+ for (curr = window_list, prev = NULL; curr; prev = curr, curr = curr->next)
+ if (curr == info)
+ {
+ if (prev)
+ prev->next = curr->next;
+ else
+ window_list = curr->next;
+ break;
+ }
+
+ // free any views
+ for (viewnum = 0; viewnum < MAX_VIEWS; viewnum++)
+ if (info->view[viewnum].view)
+ {
+ debug_view_free(info->view[viewnum].view);
+ info->view[viewnum].view = NULL;
+ }
+
+ // free our memory
+ free(info);
+}
+
+
+
+//============================================================
+// debug_window_draw_contents
+//============================================================
+
+static void debug_window_draw_contents(debugwin_info *info, HDC dc)
+{
+ RECT bounds, parent;
+ int curview, curwnd;
+
+ // fill the background with light gray
+ GetClientRect(info->wnd, &parent);
+ FillRect(dc, &parent, GetStockObject(LTGRAY_BRUSH));
+
+ // get the parent bounds in screen coords
+ ClientToScreen(info->wnd, &((POINT *)&parent)[0]);
+ ClientToScreen(info->wnd, &((POINT *)&parent)[1]);
+
+ // draw edges around all views
+ for (curview = 0; curview < MAX_VIEWS; curview++)
+ if (info->view[curview].wnd)
+ {
+ GetWindowRect(info->view[curview].wnd, &bounds);
+ bounds.top -= parent.top;
+ bounds.bottom -= parent.top;
+ bounds.left -= parent.left;
+ bounds.right -= parent.left;
+ InflateRect(&bounds, EDGE_WIDTH, EDGE_WIDTH);
+ DrawEdge(dc, &bounds, EDGE_SUNKEN, BF_RECT);
+ }
+
+ // draw edges around all children
+ if (info->editwnd)
+ {
+ GetWindowRect(info->editwnd, &bounds);
+ bounds.top -= parent.top;
+ bounds.bottom -= parent.top;
+ bounds.left -= parent.left;
+ bounds.right -= parent.left;
+ InflateRect(&bounds, EDGE_WIDTH, EDGE_WIDTH);
+ DrawEdge(dc, &bounds, EDGE_SUNKEN, BF_RECT);
+ }
+
+ for (curwnd = 0; curwnd < MAX_OTHER_WND; curwnd++)
+ if (info->otherwnd[curwnd])
+ {
+ GetWindowRect(info->otherwnd[curwnd], &bounds);
+ bounds.top -= parent.top;
+ bounds.bottom -= parent.top;
+ bounds.left -= parent.left;
+ bounds.right -= parent.left;
+ InflateRect(&bounds, EDGE_WIDTH, EDGE_WIDTH);
+ DrawEdge(dc, &bounds, EDGE_SUNKEN, BF_RECT);
+ }
+}
+
+
+
+//============================================================
+// debug_window_proc
+//============================================================
+
+static LRESULT CALLBACK debug_window_proc(HWND wnd, UINT message, WPARAM wparam, LPARAM lparam)
+{
+ debugwin_info *info = (debugwin_info *)(FPTR)GetWindowLongPtr(wnd, GWLP_USERDATA);
+
+ // handle a few messages
+ switch (message)
+ {
+ // set the info pointer
+ case WM_CREATE:
+ {
+ CREATESTRUCT *createinfo = (CREATESTRUCT *)lparam;
+ info = (debugwin_info *)createinfo->lpCreateParams;
+ SetWindowLongPtr(wnd, GWLP_USERDATA, (LONG_PTR)createinfo->lpCreateParams);
+ if (info->handler)
+ SetWindowLongPtr(wnd, GWLP_WNDPROC, (LONG_PTR)info->handler);
+ break;
+ }
+
+ // paint: draw bezels as necessary
+ case WM_PAINT:
+ {
+ PAINTSTRUCT pstruct;
+ HDC dc = BeginPaint(wnd, &pstruct);
+ debug_window_draw_contents(info, dc);
+ EndPaint(wnd, &pstruct);
+ break;
+ }
+
+ // keydown: handle debugger keys
+ case WM_KEYDOWN:
+ {
+ if ((*info->handle_key)(info, wparam, lparam))
+ info->ignore_char_lparam = lparam >> 16;
+ break;
+ }
+
+ // char: ignore chars associated with keys we've handled
+ case WM_CHAR:
+ {
+ if (info->ignore_char_lparam != (lparam >> 16))
+ return DefWindowProc(wnd, message, wparam, lparam);
+ else
+ info->ignore_char_lparam = 0;
+ break;
+ }
+
+ // activate: set the focus
+ case WM_ACTIVATE:
+ {
+ if (wparam != WA_INACTIVE && info->focuswnd != NULL)
+ SetFocus(info->focuswnd);
+ break;
+ }
+
+ // get min/max info: set the minimum window size
+ case WM_GETMINMAXINFO:
+ {
+ MINMAXINFO *minmax = (MINMAXINFO *)lparam;
+ if (info)
+ {
+ minmax->ptMinTrackSize.x = info->minwidth;
+ minmax->ptMinTrackSize.y = info->minheight;
+ minmax->ptMaxSize.x = minmax->ptMaxTrackSize.x = info->maxwidth;
+ minmax->ptMaxSize.y = minmax->ptMaxTrackSize.y = info->maxheight;
+ }
+ break;
+ }
+
+ // sizing: recompute child window locations
+ case WM_SIZING:
+ {
+ if (info->recompute_children)
+ (*info->recompute_children)(info);
+ InvalidateRect(wnd, NULL, FALSE);
+ break;
+ }
+
+ // mouse wheel: forward to the first view
+ case WM_MOUSEWHEEL:
+ {
+ int delta = (INT16)HIWORD(wparam) / WHEEL_DELTA;
+ int viewnum = 0;
+ POINT point;
+ HWND child;
+
+ // figure out which view we are hovering over
+ GetCursorPos(&point);
+ ScreenToClient(info->wnd, &point);
+ child = ChildWindowFromPoint(info->wnd, point);
+ if (child)
+ {
+ for (viewnum = 0; viewnum < MAX_VIEWS; viewnum++)
+ if (info->view[viewnum].wnd == child)
+ break;
+ if (viewnum == MAX_VIEWS)
+ break;
+ }
+
+ // send the appropriate message to this view's scrollbar
+ if (info->view[viewnum].wnd && info->view[viewnum].vscroll)
+ {
+ int message = SB_LINELEFT;
+ if (delta < 0)
+ {
+ message = SB_LINERIGHT;
+ delta = -delta;
+ }
+ while (delta > 0)
+ {
+ SendMessage(info->view[viewnum].wnd, WM_VSCROLL, message, (LPARAM)info->view[viewnum].vscroll);
+ delta--;
+ }
+ }
+ break;
+ }
+
+ // command: handle a comment
+ case WM_COMMAND:
+ if (!(*info->handle_command)(info, wparam, lparam))
+ return DefWindowProc(wnd, message, wparam, lparam);
+ break;
+
+ // close: close the window if it's not the main console
+ case WM_CLOSE:
+ if (main_console && main_console->wnd == wnd)
+ {
+ smart_show_all(FALSE);
+ debug_cpu_go(~0);
+ }
+ else
+ DestroyWindow(wnd);
+ break;
+
+ // destroy: close down the window
+ case WM_NCDESTROY:
+ debug_window_free(info);
+ break;
+
+ // everything else: defaults
+ default:
+ return DefWindowProc(wnd, message, wparam, lparam);
+ }
+
+ return 0;
+}
+
+
+
+//============================================================
+// debug_view_create
+//============================================================
+
+static int debug_view_create(debugwin_info *info, int which, int type)
+{
+ debugview_info *view = &info->view[which];
+ void *callback = (void *)debug_view_update;
+
+ // set the owner
+ view->owner = info;
+
+ // create the child view
+ view->wnd = CreateWindowEx(DEBUG_VIEW_STYLE_EX, TEXT("MAMEDebugView"), NULL, DEBUG_VIEW_STYLE,
+ 0, 0, 100, 100, info->wnd, NULL, GetModuleHandle(NULL), view);
+ if (view->wnd == NULL)
+ goto cleanup;
+
+ // create the scroll bars
+ view->hscroll = CreateWindowEx(HSCROLL_STYLE_EX, TEXT("SCROLLBAR"), NULL, HSCROLL_STYLE,
+ 0, 0, 100, CW_USEDEFAULT, view->wnd, NULL, GetModuleHandle(NULL), view);
+ view->vscroll = CreateWindowEx(VSCROLL_STYLE_EX, TEXT("SCROLLBAR"), NULL, VSCROLL_STYLE,
+ 0, 0, CW_USEDEFAULT, 100, view->wnd, NULL, GetModuleHandle(NULL), view);
+ if (view->hscroll == NULL || view->vscroll == NULL)
+ goto cleanup;
+
+ // create the debug view
+ view->view = debug_view_alloc(type);
+ if (view->view == NULL)
+ goto cleanup;
+
+ // set the update handler
+ debug_view_set_property_fct(view->view, DVP_UPDATE_CALLBACK, callback);
+ return 1;
+
+cleanup:
+ if (view->view)
+ debug_view_free(view->view);
+ if (view->hscroll)
+ DestroyWindow(view->hscroll);
+ if (view->vscroll)
+ DestroyWindow(view->vscroll);
+ if (view->wnd)
+ DestroyWindow(view->wnd);
+ return 0;
+}
+
+
+
+//============================================================
+// debug_view_set_bounds
+//============================================================
+
+static void debug_view_set_bounds(debugview_info *info, HWND parent, const RECT *newbounds)
+{
+ RECT bounds = *newbounds;
+
+ // account for the edges and set the bounds
+ if (info->wnd)
+ smart_set_window_bounds(info->wnd, parent, &bounds);
+
+ // update
+ debug_view_update(info->view);
+}
+
+
+
+//============================================================
+// debug_view_draw_contents
+//============================================================
+
+static void debug_view_draw_contents(debugview_info *view, HDC windc)
+{
+ debug_view_char *viewdata;
+ HGDIOBJ oldfont, oldbitmap;
+ UINT32 visrows, viscols;
+ COLORREF oldfgcolor;
+ UINT32 col, row;
+ HBITMAP bitmap;
+ int oldbkmode;
+ RECT client;
+ HDC dc;
+
+ // get the client rect
+ GetClientRect(view->wnd, &client);
+
+ // create a compatible DC and an offscreen bitmap
+ dc = CreateCompatibleDC(windc);
+ if (dc == NULL)
+ return;
+ bitmap = CreateCompatibleBitmap(windc, client.right, client.bottom);
+ if (bitmap == NULL)
+ {
+ DeleteDC(dc);
+ return;
+ }
+ oldbitmap = SelectObject(dc, bitmap);
+
+ // first get the visible size from the view and a pointer to the data
+ visrows = debug_view_get_property_UINT32(view->view, DVP_VISIBLE_ROWS);
+ viscols = debug_view_get_property_UINT32(view->view, DVP_VISIBLE_COLS);
+ viewdata = debug_view_get_property_ptr(view->view, DVP_VIEW_DATA);
+
+ // set the font
+ oldfont = SelectObject(dc, debug_font);
+ oldfgcolor = GetTextColor(dc);
+ oldbkmode = GetBkMode(dc);
+ SetBkMode(dc, TRANSPARENT);
+
+ // iterate over rows and columns
+ for (row = 0; row < visrows; row++)
+ {
+ int iter;
+
+ // loop twice; once to fill the background and once to draw the text
+ for (iter = 0; iter < 2; iter++)
+ {
+ COLORREF fgcolor = RGB(0x00,0x00,0x00);
+ COLORREF bgcolor = RGB(0xff,0xff,0xff);
+ HBRUSH bgbrush = NULL;
+ int last_attrib = -1;
+ TCHAR buffer[256];
+ int count = 0;
+ RECT bounds;
+
+ // initialize the text bounds
+ bounds.left = bounds.right = 0;
+ bounds.top = row * debug_font_height;
+ bounds.bottom = bounds.top + debug_font_height;
+
+ // start with a brush on iteration #0
+ if (iter == 0)
+ bgbrush = CreateSolidBrush(bgcolor);
+
+ // iterate over columns
+ for (col = 0; col < viscols; col++)
+ {
+ // if the attribute changed, adjust the colors
+ if (viewdata[col].attrib != last_attrib)
+ {
+ COLORREF oldbg = bgcolor;
+
+ // reset to standard colors
+ fgcolor = RGB(0x00,0x00,0x00);
+ bgcolor = RGB(0xff,0xff,0xff);
+
+ // pick new fg/bg colors
+ if (viewdata[col].attrib & DCA_ANCILLARY) bgcolor = RGB(0xe0,0xe0,0xe0);
+ if (viewdata[col].attrib & DCA_SELECTED) bgcolor = RGB(0xff,0x80,0x80);
+ if (viewdata[col].attrib & DCA_CURRENT) bgcolor = RGB(0xff,0xff,0x00);
+ if ((viewdata[col].attrib & DCA_SELECTED) && (viewdata[col].attrib & DCA_CURRENT)) bgcolor = RGB(0xff,0xc0,0x80);
+ if (viewdata[col].attrib & DCA_CHANGED) fgcolor = RGB(0xff,0x00,0x00);
+ if (viewdata[col].attrib & DCA_INVALID) fgcolor = RGB(0x00,0x00,0xff);
+ if (viewdata[col].attrib & DCA_DISABLED) fgcolor = RGB((GetRValue(fgcolor) + GetRValue(bgcolor)) / 2, (GetGValue(fgcolor) + GetGValue(bgcolor)) / 2, (GetBValue(fgcolor) + GetBValue(bgcolor)) / 2);
+ if (viewdata[col].attrib & DCA_COMMENT) fgcolor = RGB(0x00,0x80,0x00);
+
+ // flush any pending drawing
+ if (count > 0)
+ {
+ bounds.right = bounds.left + count * debug_font_width;
+ if (iter == 0)
+ FillRect(dc, &bounds, bgbrush);
+ else
+ ExtTextOut(dc, bounds.left, bounds.top, 0, NULL, buffer, count, NULL);
+ bounds.left = bounds.right;
+ count = 0;
+ }
+
+ // set the new colors
+ if (iter == 0 && oldbg != bgcolor)
+ {
+ DeleteObject(bgbrush);
+ bgbrush = CreateSolidBrush(bgcolor);
+ }
+ else if (iter == 1)
+ SetTextColor(dc, fgcolor);
+ last_attrib = viewdata[col].attrib;
+ }
+
+ // add this character to the buffer
+ buffer[count++] = viewdata[col].byte;
+ }
+
+ // flush any remaining stuff
+ if (count > 0)
+ {
+ bounds.right = bounds.left + count * debug_font_width;
+ if (iter == 0)
+ FillRect(dc, &bounds, bgbrush);
+ else
+ ExtTextOut(dc, bounds.left, bounds.top, 0, NULL, buffer, count, NULL);
+ }
+
+ // erase to the end of the line
+ if (iter == 0)
+ {
+ bounds.left = bounds.right;
+ bounds.right = client.right;
+ FillRect(dc, &bounds, bgbrush);
+ DeleteObject(bgbrush);
+ }
+ }
+
+ // advance viewdata
+ viewdata += viscols;
+ }
+
+ // erase anything beyond the bottom with white
+ GetClientRect(view->wnd, &client);
+ client.top = visrows * debug_font_height;
+ FillRect(dc, &client, (HBRUSH)GetStockObject(WHITE_BRUSH));
+
+ // reset the font
+ SetBkMode(dc, oldbkmode);
+ SetTextColor(dc, oldfgcolor);
+ SelectObject(dc, oldfont);
+
+ // blit the final results
+ BitBlt(windc, 0, 0, client.right, client.bottom, dc, 0, 0, SRCCOPY);
+
+ // undo the offscreen stuff
+ SelectObject(dc, oldbitmap);
+ DeleteObject(bitmap);
+ DeleteDC(dc);
+}
+
+
+
+//============================================================
+// debug_view_update
+//============================================================
+
+static void debug_view_update(debug_view *view)
+{
+ debugview_info *info = debug_view_find(view);
+
+ // if we have a view window, process it
+ if (info && info->view)
+ {
+ RECT bounds, vscroll_bounds, hscroll_bounds;
+ int show_vscroll, show_hscroll;
+ UINT32 visible_rows, visible_cols;
+ UINT32 total_rows, total_cols;
+ UINT32 top_row, left_col;
+ SCROLLINFO scrollinfo;
+
+ // get the view window bounds
+ GetClientRect(info->wnd, &bounds);
+ visible_rows = (bounds.bottom - bounds.top) / debug_font_height;
+ visible_cols = (bounds.right - bounds.left) / debug_font_width;
+
+ // get the updated total rows/cols and left row/col
+ total_rows = debug_view_get_property_UINT32(view, DVP_TOTAL_ROWS);
+ total_cols = debug_view_get_property_UINT32(view, DVP_TOTAL_COLS);
+ top_row = debug_view_get_property_UINT32(view, DVP_TOP_ROW);
+ left_col = debug_view_get_property_UINT32(view, DVP_LEFT_COL);
+
+ // determine if we need to show the scrollbars
+ show_vscroll = show_hscroll = 0;
+ if (total_rows > visible_rows && bounds.right >= vscroll_width)
+ {
+ bounds.right -= vscroll_width;
+ visible_cols = (bounds.right - bounds.left) / debug_font_width;
+ show_vscroll = TRUE;
+ }
+ if (total_cols > visible_cols && bounds.bottom >= hscroll_height)
+ {
+ bounds.bottom -= hscroll_height;
+ visible_rows = (bounds.bottom - bounds.top) / debug_font_height;
+ show_hscroll = TRUE;
+ }
+ if (!show_vscroll && total_rows > visible_rows && bounds.right >= vscroll_width)
+ {
+ bounds.right -= vscroll_width;
+ visible_cols = (bounds.right - bounds.left) / debug_font_width;
+ show_vscroll = TRUE;
+ }
+
+ // compute the bounds of the scrollbars
+ GetClientRect(info->wnd, &vscroll_bounds);
+ vscroll_bounds.left = vscroll_bounds.right - vscroll_width;
+ if (show_hscroll)
+ vscroll_bounds.bottom -= hscroll_height;
+
+ GetClientRect(info->wnd, &hscroll_bounds);
+ hscroll_bounds.top = hscroll_bounds.bottom - hscroll_height;
+ if (show_vscroll)
+ hscroll_bounds.right -= vscroll_width;
+
+ // if we hid the scrollbars, make sure we reset the top/left corners
+ if (top_row + visible_rows > total_rows)
+ top_row = (total_rows > visible_rows) ? (total_rows - visible_rows) : 0;
+ if (left_col + visible_cols > total_cols)
+ left_col = (total_cols > visible_cols) ? (total_cols - visible_cols) : 0;
+
+ // fill out the scroll info struct for the vertical scrollbar
+ scrollinfo.cbSize = sizeof(scrollinfo);
+ scrollinfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE;
+ scrollinfo.nMin = 0;
+ scrollinfo.nMax = total_rows - 1;
+ scrollinfo.nPage = visible_rows;
+ scrollinfo.nPos = top_row;
+ SetScrollInfo(info->vscroll, SB_CTL, &scrollinfo, TRUE);
+
+ // fill out the scroll info struct for the horizontal scrollbar
+ scrollinfo.cbSize = sizeof(scrollinfo);
+ scrollinfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE;
+ scrollinfo.nMin = 0;
+ scrollinfo.nMax = total_cols - 1;
+ scrollinfo.nPage = visible_cols;
+ scrollinfo.nPos = left_col;
+ SetScrollInfo(info->hscroll, SB_CTL, &scrollinfo, TRUE);
+
+ // update window info
+ visible_rows++;
+ visible_cols++;
+ debug_view_set_property_UINT32(view, DVP_VISIBLE_ROWS, visible_rows);
+ debug_view_set_property_UINT32(view, DVP_VISIBLE_COLS, visible_cols);
+ debug_view_set_property_UINT32(view, DVP_TOP_ROW, top_row);
+ debug_view_set_property_UINT32(view, DVP_LEFT_COL, left_col);
+
+ // invalidate the bounds
+ InvalidateRect(info->wnd, NULL, FALSE);
+
+ // adjust the bounds of the scrollbars and show/hide them
+ if (info->vscroll)
+ {
+ if (show_vscroll)
+ smart_set_window_bounds(info->vscroll, info->wnd, &vscroll_bounds);
+ smart_show_window(info->vscroll, show_vscroll);
+ }
+ if (info->hscroll)
+ {
+ if (show_hscroll)
+ smart_set_window_bounds(info->hscroll, info->wnd, &hscroll_bounds);
+ smart_show_window(info->hscroll, show_hscroll);
+ }
+
+ // if we're in some tight busy loop, handle messages to keep ourselves alive
+/* if (GetTickCount() - last_debugger_update > 1000)
+ {
+ MSG message;
+ while (PeekMessage(&message, NULL, 0, 0, PM_REMOVE))
+ {
+ TranslateMessage(&message);
+ DispatchMessage(&message);
+ }
+ }*/
+ }
+}
+
+
+
+//============================================================
+// debug_view_find
+//============================================================
+
+static debugview_info *debug_view_find(debug_view *view)
+{
+ debugwin_info *info;
+ int curview;
+
+ // loop over windows and find the view
+ for (info = window_list; info; info = info->next)
+ for (curview = 0; curview < MAX_VIEWS; curview++)
+ if (info->view[curview].view == view)
+ return &info->view[curview];
+ return NULL;
+}
+
+
+
+//============================================================
+// debug_view_process_scroll
+//============================================================
+
+static UINT32 debug_view_process_scroll(debugview_info *info, WORD type, HWND wnd)
+{
+ SCROLLINFO scrollinfo;
+ INT32 maxval;
+ INT32 result;
+
+ // get the current info
+ scrollinfo.cbSize = sizeof(scrollinfo);
+ scrollinfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_TRACKPOS;
+ GetScrollInfo(wnd, SB_CTL, &scrollinfo);
+
+ // by default we stay put
+ result = scrollinfo.nPos;
+
+ // determine the maximum value
+ maxval = (scrollinfo.nMax > scrollinfo.nPage) ? (scrollinfo.nMax - scrollinfo.nPage + 1) : 0;
+
+ // handle the message
+ switch (type)
+ {
+ case SB_THUMBTRACK:
+ result = scrollinfo.nTrackPos;
+ break;
+
+ case SB_LEFT:
+ result = 0;
+ break;
+
+ case SB_RIGHT:
+ result = maxval;
+ break;
+
+ case SB_LINELEFT:
+ result -= 1;
+ break;
+
+ case SB_LINERIGHT:
+ result += 1;
+ break;
+
+ case SB_PAGELEFT:
+ result -= scrollinfo.nPage - 1;
+ break;
+
+ case SB_PAGERIGHT:
+ result += scrollinfo.nPage - 1;
+ break;
+ }
+
+ // generic rangecheck
+ if (result < 0)
+ result = 0;
+ if (result > maxval)
+ result = maxval;
+
+ // set the new position
+ scrollinfo.fMask = SIF_POS;
+ scrollinfo.nPos = result;
+ SetScrollInfo(wnd, SB_CTL, &scrollinfo, TRUE);
+
+ // note if we are at the bottom
+ if (wnd == info->vscroll && info->is_textbuf && result >= maxval - 1)
+ return (UINT32)-1;
+
+ return (UINT32)result;
+}
+
+
+
+//============================================================
+// debug_view_prev_view
+//============================================================
+
+static void debug_view_prev_view(debugwin_info *info, debugview_info *curview)
+{
+ int curindex = 1;
+ int numviews;
+
+ // count the number of views
+ for (numviews = 0; numviews < MAX_VIEWS; numviews++)
+ if (info->view[numviews].wnd == NULL)
+ break;
+
+ // if we have a curview, find out its index
+ if (curview)
+ curindex = curview - &info->view[0];
+
+ // loop until we find someone to take focus
+ while (1)
+ {
+ // advance to the previous index
+ curindex--;
+ if (curindex < -1)
+ curindex = numviews - 1;
+
+ // negative numbers mean the focuswnd
+ if (curindex < 0 && info->focuswnd != NULL)
+ {
+ SetFocus(info->focuswnd);
+ break;
+ }
+
+ // positive numbers mean a view
+ else if (curindex >= 0 && info->view[curindex].wnd != NULL && debug_view_get_property_UINT32(info->view[curindex].view, DVP_SUPPORTS_CURSOR))
+ {
+ SetFocus(info->view[curindex].wnd);
+ break;
+ }
+ }
+}
+
+
+
+//============================================================
+// debug_view_next_view
+//============================================================
+
+static void debug_view_next_view(debugwin_info *info, debugview_info *curview)
+{
+ int curindex = -1;
+ int numviews;
+
+ // count the number of views
+ for (numviews = 0; numviews < MAX_VIEWS; numviews++)
+ if (info->view[numviews].wnd == NULL)
+ break;
+
+ // if we have a curview, find out its index
+ if (curview)
+ curindex = curview - &info->view[0];
+
+ // loop until we find someone to take focus
+ while (1)
+ {
+ // advance to the previous index
+ curindex++;
+ if (curindex >= numviews)
+ curindex = -1;
+
+ // negative numbers mean the focuswnd
+ if (curindex < 0 && info->focuswnd != NULL)
+ {
+ SetFocus(info->focuswnd);
+ break;
+ }
+
+ // positive numbers mean a view
+ else if (curindex >= 0 && info->view[curindex].wnd != NULL && debug_view_get_property_UINT32(info->view[curindex].view, DVP_SUPPORTS_CURSOR))
+ {
+ SetFocus(info->view[curindex].wnd);
+ InvalidateRect(info->view[curindex].wnd, NULL, FALSE);
+ break;
+ }
+ }
+}
+
+
+
+//============================================================
+// debug_view_proc
+//============================================================
+
+static LRESULT CALLBACK debug_view_proc(HWND wnd, UINT message, WPARAM wparam, LPARAM lparam)
+{
+ debugview_info *info = (debugview_info *)(FPTR)GetWindowLongPtr(wnd, GWLP_USERDATA);
+
+ // handle a few messages
+ switch (message)
+ {
+ // set the info pointer
+ case WM_CREATE:
+ {
+ CREATESTRUCT *createinfo = (CREATESTRUCT *)lparam;
+ SetWindowLongPtr(wnd, GWLP_USERDATA, (LONG_PTR)createinfo->lpCreateParams);
+ break;
+ }
+
+ // paint: redraw the last bitmap
+ case WM_PAINT:
+ {
+ PAINTSTRUCT pstruct;
+ HDC dc = BeginPaint(wnd, &pstruct);
+ debug_view_draw_contents(info, dc);
+ EndPaint(wnd, &pstruct);
+ break;
+ }
+
+ // keydown: handle debugger keys
+ case WM_KEYDOWN:
+ {
+ if ((*info->owner->handle_key)(info->owner, wparam, lparam))
+ info->owner->ignore_char_lparam = lparam >> 16;
+ else
+ {
+ switch (wparam)
+ {
+ case VK_UP:
+ debug_view_set_property_UINT32(info->view, DVP_CHARACTER, DCH_UP);
+ info->owner->ignore_char_lparam = lparam >> 16;
+ break;
+
+ case VK_DOWN:
+ debug_view_set_property_UINT32(info->view, DVP_CHARACTER, DCH_DOWN);
+ info->owner->ignore_char_lparam = lparam >> 16;
+ break;
+
+ case VK_LEFT:
+ if (GetAsyncKeyState(VK_CONTROL) & 0x8000)
+ debug_view_set_property_UINT32(info->view, DVP_CHARACTER, DCH_CTRLLEFT);
+ else
+ debug_view_set_property_UINT32(info->view, DVP_CHARACTER, DCH_LEFT);
+ info->owner->ignore_char_lparam = lparam >> 16;
+ break;
+
+ case VK_RIGHT:
+ if (GetAsyncKeyState(VK_CONTROL) & 0x8000)
+ debug_view_set_property_UINT32(info->view, DVP_CHARACTER, DCH_CTRLRIGHT);
+ else
+ debug_view_set_property_UINT32(info->view, DVP_CHARACTER, DCH_RIGHT);
+ info->owner->ignore_char_lparam = lparam >> 16;
+ break;
+
+ case VK_PRIOR:
+ debug_view_set_property_UINT32(info->view, DVP_CHARACTER, DCH_PUP);
+ info->owner->ignore_char_lparam = lparam >> 16;
+ break;
+
+ case VK_NEXT:
+ debug_view_set_property_UINT32(info->view, DVP_CHARACTER, DCH_PDOWN);
+ info->owner->ignore_char_lparam = lparam >> 16;
+ break;
+
+ case VK_HOME:
+ if (GetAsyncKeyState(VK_CONTROL) & 0x8000)
+ debug_view_set_property_UINT32(info->view, DVP_CHARACTER, DCH_CTRLHOME);
+ else
+ debug_view_set_property_UINT32(info->view, DVP_CHARACTER, DCH_HOME);
+ info->owner->ignore_char_lparam = lparam >> 16;
+ break;
+
+ case VK_END:
+ if (GetAsyncKeyState(VK_CONTROL) & 0x8000)
+ debug_view_set_property_UINT32(info->view, DVP_CHARACTER, DCH_CTRLEND);
+ else
+ debug_view_set_property_UINT32(info->view, DVP_CHARACTER, DCH_END);
+ info->owner->ignore_char_lparam = lparam >> 16;
+ break;
+
+ case VK_ESCAPE:
+ if (info->owner->focuswnd != NULL)
+ SetFocus(info->owner->focuswnd);
+ info->owner->ignore_char_lparam = lparam >> 16;
+ break;
+
+ case VK_TAB:
+ if (GetAsyncKeyState(VK_SHIFT) & 0x8000)
+ debug_view_prev_view(info->owner, info);
+ else
+ debug_view_next_view(info->owner, info);
+ info->owner->ignore_char_lparam = lparam >> 16;
+ break;
+ }
+ }
+ break;
+ }
+
+ // char: ignore chars associated with keys we've handled
+ case WM_CHAR:
+ {
+ if (info->owner->ignore_char_lparam != (lparam >> 16))
+ {
+ if (wparam >= 32 && wparam < 127 && debug_view_get_property_UINT32(info->view, DVP_SUPPORTS_CURSOR))
+ debug_view_set_property_UINT32(info->view, DVP_CHARACTER, wparam);
+ else
+ return DefWindowProc(wnd, message, wparam, lparam);
+ }
+ else
+ info->owner->ignore_char_lparam = 0;
+ break;
+ }
+
+ // gaining focus
+ case WM_SETFOCUS:
+ {
+ if (debug_view_get_property_UINT32(info->view, DVP_SUPPORTS_CURSOR))
+ debug_view_set_property_UINT32(info->view, DVP_CURSOR_VISIBLE, 1);
+ break;
+ }
+
+ // losing focus
+ case WM_KILLFOCUS:
+ {
+ if (debug_view_get_property_UINT32(info->view, DVP_SUPPORTS_CURSOR))
+ debug_view_set_property_UINT32(info->view, DVP_CURSOR_VISIBLE, 0);
+ break;
+ }
+
+ // mouse click
+ case WM_LBUTTONDOWN:
+ {
+ if (debug_view_get_property_UINT32(info->view, DVP_SUPPORTS_CURSOR))
+ {
+ int x = GET_X_LPARAM(lparam) / debug_font_width;
+ int y = GET_Y_LPARAM(lparam) / debug_font_height;
+ debug_view_begin_update(info->view);
+ debug_view_set_property_UINT32(info->view, DVP_CURSOR_ROW, debug_view_get_property_UINT32(info->view, DVP_TOP_ROW) + y);
+ debug_view_set_property_UINT32(info->view, DVP_CURSOR_COL, debug_view_get_property_UINT32(info->view, DVP_LEFT_COL) + x);
+ debug_view_end_update(info->view);
+ SetFocus(wnd);
+ }
+ break;
+ }
+
+ // hscroll
+ case WM_HSCROLL:
+ {
+ UINT32 left_col = debug_view_process_scroll(info, LOWORD(wparam), (HWND)lparam);
+ debug_view_set_property_UINT32(info->view, DVP_LEFT_COL, left_col);
+ break;
+ }
+
+ // vscroll
+ case WM_VSCROLL:
+ {
+ UINT32 top_row = debug_view_process_scroll(info, LOWORD(wparam), (HWND)lparam);
+ if (info->is_textbuf)
+ debug_view_set_property_UINT32(info->view, DVP_TEXTBUF_LINE_LOCK, top_row);
+ else
+ debug_view_set_property_UINT32(info->view, DVP_TOP_ROW, top_row);
+ break;
+ }
+
+ // everything else: defaults
+ default:
+ return DefWindowProc(wnd, message, wparam, lparam);
+ }
+
+ return 0;
+}
+
+
+
+//============================================================
+// debug_edit_proc
+//============================================================
+
+static LRESULT CALLBACK debug_edit_proc(HWND wnd, UINT message, WPARAM wparam, LPARAM lparam)
+{
+ debugwin_info *info = (debugwin_info *)(FPTR)GetWindowLongPtr(wnd, GWLP_USERDATA);
+ TCHAR buffer[MAX_EDIT_STRING];
+
+ // handle a few messages
+ switch (message)
+ {
+ // key down: handle navigation in the attached view
+ case WM_KEYDOWN:
+ switch (wparam)
+ {
+ case VK_UP:
+ if (info->last_history < info->history_count - 1)
+ info->last_history++;
+ else
+ info->last_history = 0;
+ SendMessage(wnd, WM_SETTEXT, (WPARAM)0, (LPARAM)&info->history[info->last_history][0]);
+ SendMessage(wnd, EM_SETSEL, (WPARAM)MAX_EDIT_STRING, (LPARAM)MAX_EDIT_STRING);
+ break;
+
+ case VK_DOWN:
+ if (info->last_history > 0)
+ info->last_history--;
+ else if (info->history_count > 0)
+ info->last_history = info->history_count - 1;
+ else
+ info->last_history = 0;
+ SendMessage(wnd, WM_SETTEXT, (WPARAM)0, (LPARAM)&info->history[info->last_history][0]);
+ SendMessage(wnd, EM_SETSEL, (WPARAM)MAX_EDIT_STRING, (LPARAM)MAX_EDIT_STRING);
+ break;
+
+ case VK_PRIOR:
+ if (info->view[0].wnd && info->view[0].vscroll)
+ SendMessage(info->view[0].wnd, WM_VSCROLL, SB_PAGELEFT, (LPARAM)info->view[0].vscroll);
+ break;
+
+ case VK_NEXT:
+ if (info->view[0].wnd && info->view[0].vscroll)
+ SendMessage(info->view[0].wnd, WM_VSCROLL, SB_PAGERIGHT, (LPARAM)info->view[0].vscroll);
+ break;
+
+ case VK_TAB:
+ if (GetAsyncKeyState(VK_SHIFT) & 0x8000)
+ debug_view_prev_view(info, NULL);
+ else
+ debug_view_next_view(info, NULL);
+ info->ignore_char_lparam = lparam >> 16;
+ break;
+
+ default:
+ if (!(*info->handle_key)(info, wparam, lparam))
+ return CallWindowProc(info->original_editproc, wnd, message, wparam, lparam);
+ else
+ info->ignore_char_lparam = lparam >> 16;
+ break;
+ }
+ break;
+
+ // char: handle the return key
+ case WM_CHAR:
+
+ // ignore chars associated with keys we've handled
+ if (info->ignore_char_lparam != (lparam >> 16))
+ {
+ switch (wparam)
+ {
+ case 13:
+ {
+ // fetch the text
+ SendMessage(wnd, WM_GETTEXT, (WPARAM)ARRAY_LENGTH(buffer), (LPARAM)buffer);
+
+ // add to the history if it's not a repeat of the last one
+ if (buffer[0] != 0 && _tcscmp(buffer, &info->history[0][0]))
+ {
+ memmove(&info->history[1][0], &info->history[0][0], (HISTORY_LENGTH - 1) * MAX_EDIT_STRING);
+ _tcscpy(&info->history[0][0], buffer);
+ if (info->history_count < HISTORY_LENGTH)
+ info->history_count++;
+ }
+ info->last_history = info->history_count - 1;
+
+ // process
+ if (info->process_string)
+ {
+ char *utf8_buffer = utf8_from_tstring(buffer);
+ if (utf8_buffer != NULL)
+ {
+ (*info->process_string)(info, utf8_buffer);
+ free(utf8_buffer);
+ }
+ }
+ break;
+ }
+
+ case 27:
+ {
+ // fetch the text
+ SendMessage(wnd, WM_GETTEXT, (WPARAM)sizeof(buffer), (LPARAM)buffer);
+
+ // if it's not empty, clear the text
+ if (_tcslen(buffer) > 0)
+ {
+ info->ignore_char_lparam = lparam >> 16;
+ SendMessage(wnd, WM_SETTEXT, (WPARAM)0, (LPARAM)info->edit_defstr);
+ }
+ break;
+ }
+
+ default:
+ return CallWindowProc(info->original_editproc, wnd, message, wparam, lparam);
+ }
+ }
+ else
+ info->ignore_char_lparam = 0;
+ break;
+
+ // everything else: defaults
+ default:
+ return CallWindowProc(info->original_editproc, wnd, message, wparam, lparam);
+ }
+
+ return 0;
+}
+
+
+
+//============================================================
+// generic_create_window
+//============================================================
+
+#ifdef UNUSED_FUNCTION
+static void generic_create_window(int type)
+{
+ debugwin_info *info;
+ char title[256];
+
+ // create the window
+ _snprintf(title, ARRAY_LENGTH(title), "Debug: %s [%s]", Machine->gamedrv->description, Machine->gamedrv->name);
+ info = debug_window_create(title, NULL);
+ if (info == NULL || !debug_view_create(info, 0, type))
+ return;
+
+ // set the child function
+ info->recompute_children = generic_recompute_children;
+
+ // recompute the children once to get the maxwidth
+ generic_recompute_children(info);
+
+ // position the window and recompute children again
+ SetWindowPos(info->wnd, HWND_TOP, 100, 100, info->maxwidth, 200, SWP_SHOWWINDOW);
+ generic_recompute_children(info);
+}
+#endif
+
+
+
+//============================================================
+// generic_recompute_children
+//============================================================
+
+static void generic_recompute_children(debugwin_info *info)
+{
+ RECT parent;
+ RECT bounds;
+ UINT32 width;
+
+ // get the view width
+ width = debug_view_get_property_UINT32(info->view[0].view, DVP_TOTAL_COLS);
+
+ // compute a client rect
+ bounds.top = bounds.left = 0;
+ bounds.right = width * debug_font_width + vscroll_width + 2 * EDGE_WIDTH;
+ bounds.bottom = 200;
+ AdjustWindowRectEx(&bounds, DEBUG_WINDOW_STYLE, FALSE, DEBUG_WINDOW_STYLE_EX);
+
+ // clamp the min/max size
+ info->maxwidth = bounds.right - bounds.left;
+
+ // get the parent's dimensions
+ GetClientRect(info->wnd, &parent);
+
+ // view gets the remaining space
+ InflateRect(&parent, -EDGE_WIDTH, -EDGE_WIDTH);
+ debug_view_set_bounds(&info->view[0], info->wnd, &parent);
+}
+
+
+
+//============================================================
+// log_create_window
+//============================================================
+
+static void log_create_window(void)
+{
+ debugwin_info *info;
+ char title[256];
+ UINT32 width;
+ RECT bounds;
+
+ // create the window
+ _snprintf(title, ARRAY_LENGTH(title), "Errorlog: %s [%s]", Machine->gamedrv->description, Machine->gamedrv->name);
+ info = debug_window_create(title, NULL);
+ if (info == NULL || !debug_view_create(info, 0, DVT_LOG))
+ return;
+ info->view->is_textbuf = TRUE;
+
+ // set the child function
+ info->recompute_children = generic_recompute_children;
+
+ // get the view width
+ width = debug_view_get_property_UINT32(info->view[0].view, DVP_TOTAL_COLS);
+
+ // compute a client rect
+ bounds.top = bounds.left = 0;
+ bounds.right = width * debug_font_width + vscroll_width + 2 * EDGE_WIDTH;
+ bounds.bottom = 200;
+ AdjustWindowRectEx(&bounds, DEBUG_WINDOW_STYLE, FALSE, DEBUG_WINDOW_STYLE_EX);
+
+ // clamp the min/max size
+ info->maxwidth = bounds.right - bounds.left;
+
+ // position the window at the bottom-right
+ SetWindowPos(info->wnd, HWND_TOP,
+ 100, 100,
+ bounds.right - bounds.left, bounds.bottom - bounds.top,
+ SWP_SHOWWINDOW);
+
+ // recompute the children and set focus on the edit box
+ generic_recompute_children(info);
+}
+
+
+
+//============================================================
+// memory_determine_combo_items
+//============================================================
+
+static void memory_determine_combo_items(void)
+{
+ memorycombo_item **tail = &memorycombo;
+ UINT32 cpunum, spacenum;
+ int rgnnum, itemnum;
+
+ // first add all the CPUs' address spaces
+ for (cpunum = 0; cpunum < MAX_CPU; cpunum++)
+ {
+ const debug_cpu_info *cpuinfo = debug_get_cpu_info(cpunum);
+ if (cpuinfo->valid)
+ for (spacenum = 0; spacenum < ADDRESS_SPACES; spacenum++)
+ if (cpuinfo->space[spacenum].databytes)
+ {
+ memorycombo_item *ci = malloc_or_die(sizeof(*ci));
+ memset(ci, 0, sizeof(*ci));
+ ci->cpunum = cpunum;
+ ci->spacenum = spacenum;
+ ci->prefsize = MIN(cpuinfo->space[spacenum].databytes, 8);
+ sprintf(ci->name, "CPU #%d (%s) %s memory", cpunum, cpunum_name(cpunum), address_space_names[spacenum]);
+ *tail = ci;
+ tail = &ci->next;
+ }
+ }
+
+ // then add all the memory regions
+ for (rgnnum = 0; rgnnum < MAX_MEMORY_REGIONS; rgnnum++)
+ {
+ UINT8 *base = memory_region(rgnnum);
+ UINT32 type = memory_region_type(Machine, rgnnum);
+ if (base != NULL && type > REGION_INVALID && (type - REGION_INVALID) < ARRAY_LENGTH(memory_region_names))
+ {
+ memorycombo_item *ci = malloc_or_die(sizeof(*ci));
+ UINT32 flags = memory_region_flags(Machine, rgnnum);
+ UINT8 width, little_endian;
+ memset(ci, 0, sizeof(*ci));
+ ci->base = base;
+ ci->length = memory_region_length(rgnnum);
+ width = 1 << (flags & ROMREGION_WIDTHMASK);
+ little_endian = ((flags & ROMREGION_ENDIANMASK) == ROMREGION_LE);
+ if (type >= REGION_CPU1 && type <= REGION_CPU8)
+ {
+ const debug_cpu_info *cpuinfo = debug_get_cpu_info(type - REGION_CPU1);
+ if (cpuinfo)
+ {
+ width = cpuinfo->space[ADDRESS_SPACE_PROGRAM].databytes;
+ little_endian = (cpuinfo->endianness == CPU_IS_LE);
+ }
+ }
+ ci->prefsize = MIN(width, 8);
+ ci->offset_xor = width - 1;
+ ci->little_endian = little_endian;
+ strcpy(ci->name, memory_region_names[type - REGION_INVALID]);
+ *tail = ci;
+ tail = &ci->next;
+ }
+ }
+
+ // finally add all global array symbols
+ for (itemnum = 0; itemnum < 10000; itemnum++)
+ {
+ UINT32 valsize, valcount;
+ const char *name;
+ void *base;
+
+ /* stop when we run out of items */
+ name = state_save_get_indexed_item(itemnum, &base, &valsize, &valcount);
+ if (name == NULL)
+ break;
+
+ /* if this is a single-entry global, add it */
+ if (valcount > 1 && strstr(name, "/globals/"))
+ {
+ memorycombo_item *ci = malloc_or_die(sizeof(*ci));
+ memset(ci, 0, sizeof(*ci));
+ ci->base = base;
+ ci->length = valcount * valsize;
+ ci->prefsize = MIN(valsize, 8);
+ ci->little_endian = TRUE;
+ strcpy(ci->name, strrchr(name, '/') + 1);
+ *tail = ci;
+ tail = &ci->next;
+ }
+ }
+}
+
+
+//============================================================
+// memory_update_selection
+//============================================================
+
+static void memory_update_selection(debugwin_info *info, memorycombo_item *ci)
+{
+ debug_view_set_property_UINT32(info->view[0].view, DVP_MEM_CPUNUM, ci->cpunum);
+ debug_view_set_property_UINT32(info->view[0].view, DVP_MEM_SPACENUM, ci->spacenum);
+ debug_view_set_property_ptr(info->view[0].view, DVP_MEM_RAW_BASE, ci->base);
+ debug_view_set_property_UINT32(info->view[0].view, DVP_MEM_RAW_LENGTH, ci->length);
+ debug_view_set_property_UINT32(info->view[0].view, DVP_MEM_RAW_OFFSET_XOR, ci->offset_xor);
+ debug_view_set_property_UINT32(info->view[0].view, DVP_MEM_RAW_LITTLE_ENDIAN, ci->little_endian);
+ debug_view_set_property_UINT32(info->view[0].view, DVP_MEM_BYTES_PER_CHUNK, ci->prefsize);
+ win_set_window_text_utf8(info->wnd, ci->name);
+}
+
+
+//============================================================
+// memory_create_window
+//============================================================
+
+static void memory_create_window(void)
+{
+ int curcpu = cpu_getactivecpu(), cursel = 0;
+ memorycombo_item *ci, *selci = NULL;
+ debugwin_info *info;
+ HMENU optionsmenu;
+
+ // create the window
+ info = debug_window_create("Memory", NULL);
+ if (info == NULL || !debug_view_create(info, 0, DVT_MEMORY))
+ return;
+
+ // set the handlers
+ info->handle_command = memory_handle_command;
+ info->handle_key = memory_handle_key;
+
+ // create the options menu
+ optionsmenu = CreatePopupMenu();
+ AppendMenu(optionsmenu, MF_ENABLED, ID_1_BYTE_CHUNKS, TEXT("1-byte chunks\tCtrl+1"));
+ AppendMenu(optionsmenu, MF_ENABLED, ID_2_BYTE_CHUNKS, TEXT("2-byte chunks\tCtrl+2"));
+ AppendMenu(optionsmenu, MF_ENABLED, ID_4_BYTE_CHUNKS, TEXT("4-byte chunks\tCtrl+4"));
+ AppendMenu(optionsmenu, MF_ENABLED, ID_8_BYTE_CHUNKS, TEXT("8-byte chunks\tCtrl+8"));
+ AppendMenu(optionsmenu, MF_DISABLED | MF_SEPARATOR, 0, TEXT(""));
+ AppendMenu(optionsmenu, MF_ENABLED, ID_LOGICAL_ADDRESSES, TEXT("Logical Addresses\tCtrl+L"));
+ AppendMenu(optionsmenu, MF_ENABLED, ID_PHYSICAL_ADDRESSES, TEXT("Physical Addresses\tCtrl+Y"));
+ AppendMenu(optionsmenu, MF_DISABLED | MF_SEPARATOR, 0, TEXT(""));
+ AppendMenu(optionsmenu, MF_ENABLED, ID_REVERSE_VIEW, TEXT("Reverse View\tCtrl+R"));
+ AppendMenu(optionsmenu, MF_DISABLED | MF_SEPARATOR, 0, TEXT(""));
+ AppendMenu(optionsmenu, MF_ENABLED, ID_INCREASE_MEM_WIDTH, TEXT("Increase bytes per line\tCtrl+P"));
+ AppendMenu(optionsmenu, MF_ENABLED, ID_DECREASE_MEM_WIDTH, TEXT("Decrease bytes per line\tCtrl+O"));
+ AppendMenu(GetMenu(info->wnd), MF_ENABLED | MF_POPUP, (UINT_PTR)optionsmenu, TEXT("Options"));
+ memory_update_checkmarks(info);
+
+ // set up the view to track the initial expression
+ debug_view_begin_update(info->view[0].view);
+ debug_view_set_property_string(info->view[0].view, DVP_MEM_EXPRESSION, "0");
+ strcpy(info->edit_defstr, "0");
+ debug_view_set_property_UINT32(info->view[0].view, DVP_MEM_TRACK_LIVE, 1);
+ debug_view_end_update(info->view[0].view);
+
+ // create an edit box and override its key handling
+ info->editwnd = CreateWindowEx(EDIT_BOX_STYLE_EX, TEXT("EDIT"), NULL, EDIT_BOX_STYLE,
+ 0, 0, 100, 100, info->wnd, NULL, GetModuleHandle(NULL), NULL);
+ info->original_editproc = (void *)(FPTR)GetWindowLongPtr(info->editwnd, GWLP_WNDPROC);
+ SetWindowLongPtr(info->editwnd, GWLP_USERDATA, (LONG_PTR)info);
+ SetWindowLongPtr(info->editwnd, GWLP_WNDPROC, (LONG_PTR)debug_edit_proc);
+ SendMessage(info->editwnd, WM_SETFONT, (WPARAM)debug_font, (LPARAM)FALSE);
+ SendMessage(info->editwnd, WM_SETTEXT, (WPARAM)0, (LPARAM)"0");
+ SendMessage(info->editwnd, EM_LIMITTEXT, (WPARAM)MAX_EDIT_STRING, (LPARAM)0);
+ SendMessage(info->editwnd, EM_SETSEL, (WPARAM)0, (LPARAM)-1);
+
+ // create a combo box
+ info->otherwnd[0] = CreateWindowEx(COMBO_BOX_STYLE_EX, TEXT("COMBOBOX"), NULL, COMBO_BOX_STYLE,
+ 0, 0, 100, 1000, info->wnd, NULL, GetModuleHandle(NULL), NULL);
+ SetWindowLongPtr(info->otherwnd[0], GWLP_USERDATA, (LONG_PTR)info);
+ SendMessage(info->otherwnd[0], WM_SETFONT, (WPARAM)debug_font, (LPARAM)FALSE);
+
+ // populate the combobox
+ if (memorycombo == NULL)
+ memory_determine_combo_items();
+ for (ci = memorycombo; ci; ci = ci->next)
+ {
+ int item = SendMessage(info->otherwnd[0], CB_ADDSTRING, 0, (LPARAM)ci->name);
+ if (ci->base == NULL && ci->cpunum == curcpu && ci->spacenum == ADDRESS_SPACE_PROGRAM)
+ {
+ cursel = item;
+ selci = ci;
+ }
+ }
+ SendMessage(info->otherwnd[0], CB_SETCURSEL, cursel, 0);
+
+ // set the child functions
+ info->recompute_children = memory_recompute_children;
+ info->process_string = memory_process_string;
+
+ // set the CPUnum and spacenum properties
+ if (selci == NULL)
+ selci = memorycombo;
+ memory_update_selection(info, selci);
+
+ // recompute the children once to get the maxwidth
+ memory_recompute_children(info);
+
+ // position the window and recompute children again
+ SetWindowPos(info->wnd, HWND_TOP, 100, 100, info->maxwidth, 200, SWP_SHOWWINDOW);
+ memory_recompute_children(info);
+
+ // mark the edit box as the default focus and set it
+ info->focuswnd = info->editwnd;
+ SetFocus(info->editwnd);
+}
+
+
+
+//============================================================
+// memory_recompute_children
+//============================================================
+
+static void memory_recompute_children(debugwin_info *info)
+{
+ RECT parent, memrect, editrect, comborect;
+ RECT bounds;
+ UINT32 width;
+
+ // get the view width
+ width = debug_view_get_property_UINT32(info->view[0].view, DVP_TOTAL_COLS);
+
+ // compute a client rect
+ bounds.top = bounds.left = 0;
+ bounds.right = width * debug_font_width + vscroll_width + 2 * EDGE_WIDTH;
+ bounds.bottom = 200;
+ AdjustWindowRectEx(&bounds, DEBUG_WINDOW_STYLE, FALSE, DEBUG_WINDOW_STYLE_EX);
+
+ // clamp the min/max size
+ info->maxwidth = bounds.right - bounds.left;
+
+ // get the parent's dimensions
+ GetClientRect(info->wnd, &parent);
+
+ // edit box gets half of the width
+ editrect.top = parent.top + EDGE_WIDTH;
+ editrect.bottom = editrect.top + debug_font_height + 4;
+ editrect.left = parent.left + EDGE_WIDTH;
+ editrect.right = parent.left + (parent.right - parent.left) / 2 - EDGE_WIDTH;
+
+ // combo box gets the other half of the width
+ comborect.top = editrect.top;
+ comborect.bottom = editrect.bottom;
+ comborect.left = editrect.right + 2 * EDGE_WIDTH;
+ comborect.right = parent.right - EDGE_WIDTH;
+
+ // memory view gets the rest
+ memrect.top = editrect.bottom + 2 * EDGE_WIDTH;
+ memrect.bottom = parent.bottom - EDGE_WIDTH;
+ memrect.left = parent.left + EDGE_WIDTH;
+ memrect.right = parent.right - EDGE_WIDTH;
+
+ // set the bounds of things
+ debug_view_set_bounds(&info->view[0], info->wnd, &memrect);
+ smart_set_window_bounds(info->editwnd, info->wnd, &editrect);
+ smart_set_window_bounds(info->otherwnd[0], info->wnd, &comborect);
+}
+
+
+
+//============================================================
+// memory_process_string
+//============================================================
+
+static void memory_process_string(debugwin_info *info, const char *string)
+{
+ // set the string to the memory view
+ debug_view_set_property_string(info->view[0].view, DVP_MEM_EXPRESSION, string);
+
+ // select everything in the edit text box
+ SendMessage(info->editwnd, EM_SETSEL, (WPARAM)0, (LPARAM)-1);
+
+ // update the default string to match
+ strncpy(info->edit_defstr, string, sizeof(info->edit_defstr) - 1);
+}
+
+
+
+//============================================================
+// memory_update_checkmarks
+//============================================================
+
+static void memory_update_checkmarks(debugwin_info *info)
+{
+ CheckMenuItem(GetMenu(info->wnd), ID_1_BYTE_CHUNKS, MF_BYCOMMAND | (debug_view_get_property_UINT32(info->view[0].view, DVP_MEM_BYTES_PER_CHUNK) == 1? MF_CHECKED : MF_UNCHECKED));
+ CheckMenuItem(GetMenu(info->wnd), ID_2_BYTE_CHUNKS, MF_BYCOMMAND | (debug_view_get_property_UINT32(info->view[0].view, DVP_MEM_BYTES_PER_CHUNK) == 2 ? MF_CHECKED : MF_UNCHECKED));
+ CheckMenuItem(GetMenu(info->wnd), ID_4_BYTE_CHUNKS, MF_BYCOMMAND | (debug_view_get_property_UINT32(info->view[0].view, DVP_MEM_BYTES_PER_CHUNK) == 4 ? MF_CHECKED : MF_UNCHECKED));
+ CheckMenuItem(GetMenu(info->wnd), ID_8_BYTE_CHUNKS, MF_BYCOMMAND | (debug_view_get_property_UINT32(info->view[0].view, DVP_MEM_BYTES_PER_CHUNK) == 8 ? MF_CHECKED : MF_UNCHECKED));
+ CheckMenuItem(GetMenu(info->wnd), ID_LOGICAL_ADDRESSES, MF_BYCOMMAND | (debug_view_get_property_UINT32(info->view[0].view, DVP_MEM_NO_TRANSLATION) ? MF_UNCHECKED : MF_CHECKED));
+ CheckMenuItem(GetMenu(info->wnd), ID_PHYSICAL_ADDRESSES, MF_BYCOMMAND | (debug_view_get_property_UINT32(info->view[0].view, DVP_MEM_NO_TRANSLATION) ? MF_CHECKED : MF_UNCHECKED));
+ CheckMenuItem(GetMenu(info->wnd), ID_REVERSE_VIEW, MF_BYCOMMAND | (debug_view_get_property_UINT32(info->view[0].view, DVP_MEM_REVERSE_VIEW) ? MF_CHECKED : MF_UNCHECKED));
+}
+
+
+
+//============================================================
+// memory_handle_command
+//============================================================
+
+static int memory_handle_command(debugwin_info *info, WPARAM wparam, LPARAM lparam)
+{
+ switch (HIWORD(wparam))
+ {
+ // combo box selection changed
+ case CBN_SELCHANGE:
+ {
+ int sel = SendMessage((HWND)lparam, CB_GETCURSEL, 0, 0);
+ if (sel != CB_ERR)
+ {
+ // find the matching entry
+ memorycombo_item *ci;
+ for (ci = memorycombo; ci; ci = ci->next)
+ if (sel-- == 0)
+ {
+ debug_view_begin_update(info->view[0].view);
+ memory_update_selection(info, ci);
+ debug_view_end_update(info->view[0].view);
+ }
+
+ // reset the focus
+ SetFocus(info->focuswnd);
+ return 1;
+ }
+ break;
+ }
+
+ // menu selections
+ case 0:
+ switch (LOWORD(wparam))
+ {
+ case ID_1_BYTE_CHUNKS:
+ debug_view_begin_update(info->view[0].view);
+ debug_view_set_property_UINT32(info->view[0].view, DVP_MEM_BYTES_PER_CHUNK, 1);
+ debug_view_end_update(info->view[0].view);
+ memory_update_checkmarks(info);
+ return 1;
+
+ case ID_2_BYTE_CHUNKS:
+ debug_view_begin_update(info->view[0].view);
+ debug_view_set_property_UINT32(info->view[0].view, DVP_MEM_BYTES_PER_CHUNK, 2);
+ debug_view_end_update(info->view[0].view);
+ memory_update_checkmarks(info);
+ return 1;
+
+ case ID_4_BYTE_CHUNKS:
+ debug_view_begin_update(info->view[0].view);
+ debug_view_set_property_UINT32(info->view[0].view, DVP_MEM_BYTES_PER_CHUNK, 4);
+ debug_view_end_update(info->view[0].view);
+ memory_update_checkmarks(info);
+ return 1;
+
+ case ID_8_BYTE_CHUNKS:
+ debug_view_begin_update(info->view[0].view);
+ debug_view_set_property_UINT32(info->view[0].view, DVP_MEM_BYTES_PER_CHUNK, 8);
+ debug_view_end_update(info->view[0].view);
+ memory_update_checkmarks(info);
+ return 1;
+
+ case ID_LOGICAL_ADDRESSES:
+ debug_view_begin_update(info->view[0].view);
+ debug_view_set_property_UINT32(info->view[0].view, DVP_MEM_NO_TRANSLATION, FALSE);
+ debug_view_end_update(info->view[0].view);
+ memory_update_checkmarks(info);
+ return 1;
+
+ case ID_PHYSICAL_ADDRESSES:
+ debug_view_begin_update(info->view[0].view);
+ debug_view_set_property_UINT32(info->view[0].view, DVP_MEM_NO_TRANSLATION, TRUE);
+ debug_view_end_update(info->view[0].view);
+ memory_update_checkmarks(info);
+ return 1;
+
+ case ID_REVERSE_VIEW:
+ debug_view_begin_update(info->view[0].view);
+ debug_view_set_property_UINT32(info->view[0].view, DVP_MEM_REVERSE_VIEW, !debug_view_get_property_UINT32(info->view[0].view, DVP_MEM_REVERSE_VIEW));
+ debug_view_end_update(info->view[0].view);
+ memory_update_checkmarks(info);
+ return 1;
+
+ case ID_INCREASE_MEM_WIDTH:
+ debug_view_begin_update(info->view[0].view);
+ debug_view_set_property_UINT32(info->view[0].view, DVP_MEM_WIDTH, debug_view_get_property_UINT32(info->view[0].view, DVP_MEM_WIDTH) + 1);
+ debug_view_end_update(info->view[0].view);
+ return 1;
+
+ case ID_DECREASE_MEM_WIDTH:
+ debug_view_begin_update(info->view[0].view);
+ debug_view_set_property_UINT32(info->view[0].view, DVP_MEM_WIDTH, debug_view_get_property_UINT32(info->view[0].view, DVP_MEM_WIDTH) - 1);
+ debug_view_end_update(info->view[0].view);
+ return 1;
+ }
+ break;
+ }
+ return global_handle_command(info, wparam, lparam);
+}
+
+
+
+//============================================================
+// memory_handle_key
+//============================================================
+
+static int memory_handle_key(debugwin_info *info, WPARAM wparam, LPARAM lparam)
+{
+ if (GetAsyncKeyState(VK_CONTROL) & 0x8000)
+ {
+ switch (wparam)
+ {
+ case '1':
+ SendMessage(info->wnd, WM_COMMAND, ID_1_BYTE_CHUNKS, 0);
+ return 1;
+
+ case '2':
+ SendMessage(info->wnd, WM_COMMAND, ID_2_BYTE_CHUNKS, 0);
+ return 1;
+
+ case '4':
+ SendMessage(info->wnd, WM_COMMAND, ID_4_BYTE_CHUNKS, 0);
+ return 1;
+
+ case '8':
+ SendMessage(info->wnd, WM_COMMAND, ID_8_BYTE_CHUNKS, 0);
+ return 1;
+
+ case 'L':
+ SendMessage(info->wnd, WM_COMMAND, ID_LOGICAL_ADDRESSES, 0);
+ return 1;
+
+ case 'Y':
+ SendMessage(info->wnd, WM_COMMAND, ID_PHYSICAL_ADDRESSES, 0);
+ return 1;
+
+ case 'R':
+ SendMessage(info->wnd, WM_COMMAND, ID_REVERSE_VIEW, 0);
+ return 1;
+
+ case 'P':
+ SendMessage(info->wnd, WM_COMMAND, ID_INCREASE_MEM_WIDTH, 0);
+ return 1;
+
+ case 'O':
+ SendMessage(info->wnd, WM_COMMAND, ID_DECREASE_MEM_WIDTH, 0);
+ return 1;
+ }
+ }
+ return global_handle_key(info, wparam, lparam);
+}
+
+
+
+//============================================================
+// disasm_create_window
+//============================================================
+
+static void disasm_create_window(void)
+{
+ int curcpu = cpu_getactivecpu(), cursel = 0;
+ debugwin_info *info;
+ HMENU optionsmenu;
+ UINT32 cpunum;
+
+ // create the window
+ info = debug_window_create("Disassembly", NULL);
+ if (info == NULL || !debug_view_create(info, 0, DVT_DISASSEMBLY))
+ return;
+
+ // create the options menu
+ optionsmenu = CreatePopupMenu();
+ AppendMenu(optionsmenu, MF_ENABLED, ID_TOGGLE_BREAKPOINT, TEXT("Set breakpoint at cursor\tF9"));
+ AppendMenu(optionsmenu, MF_ENABLED, ID_RUN_TO_CURSOR, TEXT("Run to cursor\tF4"));
+ AppendMenu(optionsmenu, MF_DISABLED | MF_SEPARATOR, 0, TEXT(""));
+ AppendMenu(optionsmenu, MF_ENABLED, ID_SHOW_RAW, TEXT("Raw opcodes\tCtrl+R"));
+ AppendMenu(optionsmenu, MF_ENABLED, ID_SHOW_ENCRYPTED, TEXT("Encrypted opcodes\tCtrl+E"));
+ AppendMenu(optionsmenu, MF_ENABLED, ID_SHOW_COMMENTS, TEXT("Comments\tCtrl+M"));
+ AppendMenu(GetMenu(info->wnd), MF_ENABLED | MF_POPUP, (UINT_PTR)optionsmenu, TEXT("Options"));
+ disasm_update_checkmarks(info);
+
+ // set the handlers
+ info->handle_command = disasm_handle_command;
+ info->handle_key = disasm_handle_key;
+
+ // set up the view to track the initial expression
+ debug_view_begin_update(info->view[0].view);
+ debug_view_set_property_string(info->view[0].view, DVP_DASM_EXPRESSION, "curpc");
+ strcpy(info->edit_defstr, "curpc");
+ debug_view_set_property_UINT32(info->view[0].view, DVP_DASM_TRACK_LIVE, 1);
+ debug_view_end_update(info->view[0].view);
+
+ // create an edit box and override its key handling
+ info->editwnd = CreateWindowEx(EDIT_BOX_STYLE_EX, TEXT("EDIT"), NULL, EDIT_BOX_STYLE,
+ 0, 0, 100, 100, info->wnd, NULL, GetModuleHandle(NULL), NULL);
+ info->original_editproc = (void *)(FPTR)GetWindowLongPtr(info->editwnd, GWLP_WNDPROC);
+ SetWindowLongPtr(info->editwnd, GWLP_USERDATA, (LONG_PTR)info);
+ SetWindowLongPtr(info->editwnd, GWLP_WNDPROC, (LONG_PTR)debug_edit_proc);
+ SendMessage(info->editwnd, WM_SETFONT, (WPARAM)debug_font, (LPARAM)FALSE);
+ SendMessage(info->editwnd, WM_SETTEXT, (WPARAM)0, (LPARAM)"curpc");
+ SendMessage(info->editwnd, EM_LIMITTEXT, (WPARAM)MAX_EDIT_STRING, (LPARAM)0);
+ SendMessage(info->editwnd, EM_SETSEL, (WPARAM)0, (LPARAM)-1);
+
+ // create a combo box
+ info->otherwnd[0] = CreateWindowEx(COMBO_BOX_STYLE_EX, TEXT("COMBOBOX"), NULL, COMBO_BOX_STYLE,
+ 0, 0, 100, 100, info->wnd, NULL, GetModuleHandle(NULL), NULL);
+ SetWindowLongPtr(info->otherwnd[0], GWLP_USERDATA, (LONG_PTR)info);
+ SendMessage(info->otherwnd[0], WM_SETFONT, (WPARAM)debug_font, (LPARAM)FALSE);
+
+ // populate the combobox
+ for (cpunum = 0; cpunum < MAX_CPU; cpunum++)
+ {
+ const debug_cpu_info *cpuinfo = debug_get_cpu_info(cpunum);
+ if (cpuinfo->valid)
+ if (cpuinfo->space[ADDRESS_SPACE_PROGRAM].databytes)
+ {
+ char name[100];
+ int item;
+ sprintf(name, "CPU #%d (%s)", cpunum, cpunum_name(cpunum));
+ item = SendMessage(info->otherwnd[0], CB_ADDSTRING, 0, (LPARAM)name);
+ if (cpunum == curcpu)
+ cursel = item;
+ }
+ }
+ SendMessage(info->otherwnd[0], CB_SETCURSEL, cursel, 0);
+
+ // set the child functions
+ info->recompute_children = disasm_recompute_children;
+ info->process_string = disasm_process_string;
+
+ // set the CPUnum and spacenum properties
+ debug_view_set_property_UINT32(info->view[0].view, DVP_DASM_CPUNUM, (curcpu == -1) ? 0 : curcpu);
+
+ // set the caption
+ disasm_update_caption(info->wnd);
+
+ // recompute the children once to get the maxwidth
+ disasm_recompute_children(info);
+
+ // position the window and recompute children again
+ SetWindowPos(info->wnd, HWND_TOP, 100, 100, info->maxwidth, 200, SWP_SHOWWINDOW);
+ disasm_recompute_children(info);
+
+ // mark the edit box as the default focus and set it
+ info->focuswnd = info->editwnd;
+ SetFocus(info->editwnd);
+}
+
+
+
+//============================================================
+// disasm_recompute_children
+//============================================================
+
+static void disasm_recompute_children(debugwin_info *info)
+{
+ RECT parent, dasmrect, editrect, comborect;
+ RECT bounds;
+ UINT32 width;
+
+ // get the view width
+ width = debug_view_get_property_UINT32(info->view[0].view, DVP_TOTAL_COLS);
+
+ // compute a client rect
+ bounds.top = bounds.left = 0;
+ bounds.right = width * debug_font_width + vscroll_width + 2 * EDGE_WIDTH;
+ bounds.bottom = 200;
+ AdjustWindowRectEx(&bounds, DEBUG_WINDOW_STYLE, FALSE, DEBUG_WINDOW_STYLE_EX);
+
+ // clamp the min/max size
+ info->maxwidth = bounds.right - bounds.left;
+
+ // get the parent's dimensions
+ GetClientRect(info->wnd, &parent);
+
+ // edit box gets half of the width
+ editrect.top = parent.top + EDGE_WIDTH;
+ editrect.bottom = editrect.top + debug_font_height + 4;
+ editrect.left = parent.left + EDGE_WIDTH;
+ editrect.right = parent.left + (parent.right - parent.left) / 2 - EDGE_WIDTH;
+
+ // combo box gets the other half of the width
+ comborect.top = editrect.top;
+ comborect.bottom = editrect.bottom;
+ comborect.left = editrect.right + 2 * EDGE_WIDTH;
+ comborect.right = parent.right - EDGE_WIDTH;
+
+ // disasm view gets the rest
+ dasmrect.top = editrect.bottom + 2 * EDGE_WIDTH;
+ dasmrect.bottom = parent.bottom - EDGE_WIDTH;
+ dasmrect.left = parent.left + EDGE_WIDTH;
+ dasmrect.right = parent.right - EDGE_WIDTH;
+
+ // set the bounds of things
+ debug_view_set_bounds(&info->view[0], info->wnd, &dasmrect);
+ smart_set_window_bounds(info->editwnd, info->wnd, &editrect);
+ smart_set_window_bounds(info->otherwnd[0], info->wnd, &comborect);
+}
+
+
+
+//============================================================
+// disasm_process_string
+//============================================================
+
+static void disasm_process_string(debugwin_info *info, const char *string)
+{
+ // set the string to the disasm view
+ debug_view_set_property_string(info->view[0].view, DVP_DASM_EXPRESSION, string);
+
+ // select everything in the edit text box
+ SendMessage(info->editwnd, EM_SETSEL, (WPARAM)0, (LPARAM)-1);
+
+ // update the default string to match
+ strncpy(info->edit_defstr, string, sizeof(info->edit_defstr) - 1);
+}
+
+
+
+//============================================================
+// disasm_update_checkmarks
+//============================================================
+
+static void disasm_update_checkmarks(debugwin_info *info)
+{
+ int rightcol = debug_view_get_property_UINT32(info->view[0].view, DVP_DASM_RIGHT_COLUMN);
+ CheckMenuItem(GetMenu(info->wnd), ID_SHOW_RAW, MF_BYCOMMAND | (rightcol == DVP_DASM_RIGHTCOL_RAW ? MF_CHECKED : MF_UNCHECKED));
+ CheckMenuItem(GetMenu(info->wnd), ID_SHOW_ENCRYPTED, MF_BYCOMMAND | (rightcol == DVP_DASM_RIGHTCOL_ENCRYPTED ? MF_CHECKED : MF_UNCHECKED));
+ CheckMenuItem(GetMenu(info->wnd), ID_SHOW_COMMENTS, MF_BYCOMMAND | (rightcol == DVP_DASM_RIGHTCOL_COMMENTS ? MF_CHECKED : MF_UNCHECKED));
+}
+
+
+
+//============================================================
+// disasm_handle_command
+//============================================================
+
+static int disasm_handle_command(debugwin_info *info, WPARAM wparam, LPARAM lparam)
+{
+ char command[64];
+ UINT32 active_address = 0x00;
+
+ switch (HIWORD(wparam))
+ {
+ // combo box selection changed
+ case CBN_SELCHANGE:
+ {
+ int sel = SendMessage((HWND)lparam, CB_GETCURSEL, 0, 0);
+ if (sel != CB_ERR)
+ {
+ // find the matching entry
+ UINT32 cpunum;
+ for (cpunum = 0; cpunum < MAX_CPU; cpunum++)
+ {
+ const debug_cpu_info *cpuinfo = debug_get_cpu_info(cpunum);
+ if (cpuinfo->valid)
+ if (cpuinfo->space[ADDRESS_SPACE_PROGRAM].databytes)
+ if (sel-- == 0)
+ {
+ debug_view_begin_update(info->view[0].view);
+ debug_view_set_property_UINT32(info->view[0].view, DVP_DASM_CPUNUM, cpunum);
+ debug_view_end_update(info->view[0].view);
+ disasm_update_caption(info->wnd);
+ }
+ }
+
+ // reset the focus
+ SetFocus(info->focuswnd);
+ return 1;
+ }
+ break;
+ }
+
+ // menu selections
+ case 0:
+ switch (LOWORD(wparam))
+ {
+ case ID_SHOW_RAW:
+ debug_view_begin_update(info->view[0].view);
+ debug_view_set_property_UINT32(info->view[0].view, DVP_DASM_RIGHT_COLUMN, DVP_DASM_RIGHTCOL_RAW);
+ debug_view_end_update(info->view[0].view);
+ disasm_update_checkmarks(info);
+ (*info->recompute_children)(info);
+ return 1;
+
+ case ID_SHOW_ENCRYPTED:
+ debug_view_begin_update(info->view[0].view);
+ debug_view_set_property_UINT32(info->view[0].view, DVP_DASM_RIGHT_COLUMN, DVP_DASM_RIGHTCOL_ENCRYPTED);
+ debug_view_end_update(info->view[0].view);
+ disasm_update_checkmarks(info);
+ (*info->recompute_children)(info);
+ return 1;
+
+ case ID_SHOW_COMMENTS:
+ debug_view_begin_update(info->view[0].view);
+ debug_view_set_property_UINT32(info->view[0].view, DVP_DASM_RIGHT_COLUMN, DVP_DASM_RIGHTCOL_COMMENTS);
+ debug_view_end_update(info->view[0].view);
+ disasm_update_checkmarks(info);
+ (*info->recompute_children)(info);
+ return 1;
+
+ case ID_RUN_TO_CURSOR:
+ if (debug_view_get_property_UINT32(info->view[0].view, DVP_CURSOR_VISIBLE))
+ {
+ UINT32 cpu_num = 0;
+ debug_cpu_info *cpuinfo ;
+
+ /* for BYTE2ADDR */
+ cpu_num = debug_view_get_property_UINT32(info->view[0].view, DVP_DASM_CPUNUM);
+ cpuinfo = (debug_cpu_info *)debug_get_cpu_info(cpu_num);
+
+ active_address = debug_view_get_property_UINT32(info->view[0].view, DVP_DASM_ACTIVE_ADDRESS);
+ sprintf(command, "go %X", BYTE2ADDR(active_address, cpuinfo, ADDRESS_SPACE_PROGRAM));
+ debug_console_execute_command(command, 1);
+ }
+ return 1;
+
+ case ID_TOGGLE_BREAKPOINT:
+ if (debug_view_get_property_UINT32(info->view[0].view, DVP_CURSOR_VISIBLE))
+ {
+ UINT32 cpu_num = 0;
+ debug_cpu_info *cpuinfo ;
+ debug_cpu_breakpoint *bp;
+ INT8 bp_exists = 0;
+ UINT32 bp_num = 0;
+
+ /* what address are we dealing with? */
+ active_address = debug_view_get_property_UINT32(info->view[0].view, DVP_DASM_ACTIVE_ADDRESS);
+
+ /* is there already a breakpoint there? */
+ cpu_num = debug_view_get_property_UINT32(info->view[0].view, DVP_DASM_CPUNUM);
+ cpuinfo = (debug_cpu_info*)debug_get_cpu_info(cpu_num);
+
+ for (bp = cpuinfo->first_bp; bp; bp = bp->next)
+ {
+ if (BYTE2ADDR(active_address, cpuinfo, ADDRESS_SPACE_PROGRAM) == bp->address)
+ {
+ bp_exists = 1;
+ bp_num = bp->index;
+ }
+ }
+
+ /* Toggle */
+ if (!bp_exists)
+ sprintf(command, "bpset %X", BYTE2ADDR(active_address, cpuinfo, ADDRESS_SPACE_PROGRAM));
+ else
+ sprintf(command, "bpclear %X", bp_num);
+ debug_console_execute_command(command, 1);
+ }
+ return 1;
+ }
+ break;
+ }
+ return global_handle_command(info, wparam, lparam);
+}
+
+
+
+//============================================================
+// disasm_handle_key
+//============================================================
+
+static int disasm_handle_key(debugwin_info *info, WPARAM wparam, LPARAM lparam)
+{
+ if (GetAsyncKeyState(VK_CONTROL) & 0x8000)
+ {
+ switch (wparam)
+ {
+ case 'R':
+ SendMessage(info->wnd, WM_COMMAND, ID_SHOW_RAW, 0);
+ return 1;
+
+ case 'E':
+ SendMessage(info->wnd, WM_COMMAND, ID_SHOW_ENCRYPTED, 0);
+ return 1;
+
+ case 'N':
+ SendMessage(info->wnd, WM_COMMAND, ID_SHOW_COMMENTS, 0);
+ return 1;
+ }
+ }
+
+ switch (wparam)
+ {
+ /* ajg - steals the F4 from the global key handler - but ALT+F4 didn't work anyways ;) */
+ case VK_F4:
+ SendMessage(info->wnd, WM_COMMAND, ID_RUN_TO_CURSOR, 0);
+ return 1;
+
+ case VK_F9:
+ SendMessage(info->wnd, WM_COMMAND, ID_TOGGLE_BREAKPOINT, 0);
+ return 1;
+
+ case VK_RETURN:
+ if (debug_view_get_property_UINT32(info->view[0].view, DVP_CURSOR_VISIBLE))
+ {
+ SendMessage(info->wnd, WM_COMMAND, ID_STEP, 0);
+ return 1;
+ }
+ break;
+ }
+
+ return global_handle_key(info, wparam, lparam);
+}
+
+
+
+//============================================================
+// disasm_update_caption
+//============================================================
+
+static void disasm_update_caption(HWND wnd)
+{
+ debugwin_info *info = (debugwin_info *)(FPTR)GetWindowLongPtr(wnd, GWLP_USERDATA);
+ char title[100];
+ UINT32 cpunum;
+
+ // get the properties
+ cpunum = debug_view_get_property_UINT32(info->view[0].view, DVP_DASM_CPUNUM);
+
+ // then update the caption
+ sprintf(title, "Disassembly: %s (%d)", cpunum_name(cpunum), cpunum);
+ win_set_window_text_utf8(wnd, title);
+}
+
+
+
+//============================================================
+// console_create_window
+//============================================================
+
+void console_create_window(void)
+{
+ debugwin_info *info;
+ int bestwidth, bestheight;
+ RECT bounds, work_bounds;
+ HMENU optionsmenu;
+ UINT32 cpunum;
+
+ // create the window
+ info = debug_window_create("Debug", NULL);
+ if (info == NULL)
+ return;
+ main_console = info;
+ console_set_cpunum(0);
+
+ // create the views
+ if (!debug_view_create(info, 0, DVT_DISASSEMBLY))
+ goto cleanup;
+ if (!debug_view_create(info, 1, DVT_REGISTERS))
+ goto cleanup;
+ if (!debug_view_create(info, 2, DVT_CONSOLE))
+ goto cleanup;
+
+ // create the options menu
+ optionsmenu = CreatePopupMenu();
+ AppendMenu(optionsmenu, MF_ENABLED, ID_TOGGLE_BREAKPOINT, TEXT("Set breakpoint at cursor\tF9"));
+ AppendMenu(optionsmenu, MF_ENABLED, ID_RUN_TO_CURSOR, TEXT("Run to cursor\tF4"));
+ AppendMenu(optionsmenu, MF_DISABLED | MF_SEPARATOR, 0, TEXT(""));
+ AppendMenu(optionsmenu, MF_ENABLED, ID_SHOW_RAW, TEXT("Raw opcodes\tCtrl+R"));
+ AppendMenu(optionsmenu, MF_ENABLED, ID_SHOW_ENCRYPTED, TEXT("Encrypted opcodes\tCtrl+E"));
+ AppendMenu(optionsmenu, MF_ENABLED, ID_SHOW_COMMENTS, TEXT("Comments\tCtrl+N"));
+ AppendMenu(GetMenu(info->wnd), MF_ENABLED | MF_POPUP, (UINT_PTR)optionsmenu, TEXT("Options"));
+ disasm_update_checkmarks(info);
+
+ // set the handlers
+ info->handle_command = disasm_handle_command;
+ info->handle_key = disasm_handle_key;
+
+ // lock us to the bottom of the console by default
+ info->view[2].is_textbuf = TRUE;
+
+ // set up the disassembly view to track the current pc
+ debug_view_begin_update(info->view[0].view);
+ debug_view_set_property_string(info->view[0].view, DVP_DASM_EXPRESSION, "curpc");
+ debug_view_set_property_UINT32(info->view[0].view, DVP_DASM_TRACK_LIVE, 1);
+ debug_view_end_update(info->view[0].view);
+
+ // create an edit box and override its key handling
+ info->editwnd = CreateWindowEx(EDIT_BOX_STYLE_EX, TEXT("EDIT"), NULL, EDIT_BOX_STYLE,
+ 0, 0, 100, 100, info->wnd, NULL, GetModuleHandle(NULL), NULL);
+ info->original_editproc = (void *)(FPTR)GetWindowLongPtr(info->editwnd, GWLP_WNDPROC);
+ SetWindowLongPtr(info->editwnd, GWLP_USERDATA, (LONG_PTR)info);
+ SetWindowLongPtr(info->editwnd, GWLP_WNDPROC, (LONG_PTR)debug_edit_proc);
+ SendMessage(info->editwnd, WM_SETFONT, (WPARAM)debug_font, (LPARAM)FALSE);
+ SendMessage(info->editwnd, EM_LIMITTEXT, (WPARAM)MAX_EDIT_STRING, (LPARAM)0);
+
+ // set the child functions
+ info->recompute_children = console_recompute_children;
+ info->process_string = console_process_string;
+
+ // loop over all CPUs and compute the sizes
+ info->minwidth = 0;
+ info->maxwidth = 0;
+ for (cpunum = MAX_CPU - 1; (INT32)cpunum >= 0; cpunum--)
+ if (Machine->drv->cpu[cpunum].type != CPU_DUMMY)
+ {
+ UINT32 regchars, dischars, conchars;
+ UINT32 minwidth, maxwidth;
+
+ // point all views to the new CPU number
+ debug_view_set_property_UINT32(info->view[0].view, DVP_DASM_CPUNUM, cpunum);
+ debug_view_set_property_UINT32(info->view[1].view, DVP_REGS_CPUNUM, cpunum);
+
+ // get the total width of all three children
+ dischars = debug_view_get_property_UINT32(info->view[0].view, DVP_TOTAL_COLS);
+ regchars = debug_view_get_property_UINT32(info->view[1].view, DVP_TOTAL_COLS);
+ conchars = debug_view_get_property_UINT32(info->view[2].view, DVP_TOTAL_COLS);
+
+ // compute the preferred width
+ minwidth = EDGE_WIDTH + regchars * debug_font_width + vscroll_width + 2 * EDGE_WIDTH + 100 + EDGE_WIDTH;
+ maxwidth = EDGE_WIDTH + regchars * debug_font_width + vscroll_width + 2 * EDGE_WIDTH + ((dischars > conchars) ? dischars : conchars) * debug_font_width + vscroll_width + EDGE_WIDTH;
+ if (minwidth > info->minwidth)
+ info->minwidth = minwidth;
+ if (maxwidth > info->maxwidth)
+ info->maxwidth = maxwidth;
+ }
+
+ // get the work bounds
+ SystemParametersInfo(SPI_GETWORKAREA, 0, &work_bounds, 0);
+
+ // adjust the min/max sizes for the window style
+ bounds.top = bounds.left = 0;
+ bounds.right = bounds.bottom = info->minwidth;
+ AdjustWindowRectEx(&bounds, DEBUG_WINDOW_STYLE, FALSE, DEBUG_WINDOW_STYLE_EX);
+ info->minwidth = bounds.right - bounds.left;
+
+ bounds.top = bounds.left = 0;
+ bounds.right = bounds.bottom = info->maxwidth;
+ AdjustWindowRectEx(&bounds, DEBUG_WINDOW_STYLE, FALSE, DEBUG_WINDOW_STYLE_EX);
+ info->maxwidth = bounds.right - bounds.left;
+
+ // position the window at the bottom-right
+ bestwidth = (info->maxwidth < (work_bounds.right - work_bounds.left)) ? info->maxwidth : (work_bounds.right - work_bounds.left);
+ bestheight = (500 < (work_bounds.bottom - work_bounds.top)) ? 500 : (work_bounds.bottom - work_bounds.top);
+ SetWindowPos(info->wnd, HWND_TOP,
+ work_bounds.right - bestwidth, work_bounds.bottom - bestheight,
+ bestwidth, bestheight,
+ SWP_SHOWWINDOW);
+
+ // recompute the children
+ console_recompute_children(info);
+
+ // mark the edit box as the default focus and set it
+ info->focuswnd = info->editwnd;
+ SetFocus(info->editwnd);
+ return;
+
+cleanup:
+ if (info->view[2].view)
+ debug_view_free(info->view[2].view);
+ if (info->view[1].view)
+ debug_view_free(info->view[1].view);
+ if (info->view[0].view)
+ debug_view_free(info->view[0].view);
+}
+
+
+
+//============================================================
+// console_recompute_children
+//============================================================
+
+static void console_recompute_children(debugwin_info *info)
+{
+ RECT parent, regrect, disrect, conrect, editrect;
+ UINT32 regchars, dischars, conchars;
+
+ // get the parent's dimensions
+ GetClientRect(info->wnd, &parent);
+
+ // get the total width of all three children
+ dischars = debug_view_get_property_UINT32(info->view[0].view, DVP_TOTAL_COLS);
+ regchars = debug_view_get_property_UINT32(info->view[1].view, DVP_TOTAL_COLS);
+ conchars = debug_view_get_property_UINT32(info->view[2].view, DVP_TOTAL_COLS);
+
+ // registers always get their desired width, and span the entire height
+ regrect.top = parent.top + EDGE_WIDTH;
+ regrect.bottom = parent.bottom - EDGE_WIDTH;
+ regrect.left = parent.left + EDGE_WIDTH;
+ regrect.right = regrect.left + debug_font_width * regchars + vscroll_width;
+
+ // edit box goes at the bottom of the remaining area
+ editrect.bottom = parent.bottom - EDGE_WIDTH;
+ editrect.top = editrect.bottom - debug_font_height - 4;
+ editrect.left = regrect.right + EDGE_WIDTH * 2;
+ editrect.right = parent.right - EDGE_WIDTH;
+
+ // console and disassembly split the difference
+ disrect.top = parent.top + EDGE_WIDTH;
+ disrect.bottom = (editrect.top - parent.top) / 2 - EDGE_WIDTH;
+ disrect.left = regrect.right + EDGE_WIDTH * 2;
+ disrect.right = parent.right - EDGE_WIDTH;
+
+ conrect.top = disrect.bottom + EDGE_WIDTH * 2;
+ conrect.bottom = editrect.top - EDGE_WIDTH;
+ conrect.left = regrect.right + EDGE_WIDTH * 2;
+ conrect.right = parent.right - EDGE_WIDTH;
+
+ // set the bounds of things
+ debug_view_set_bounds(&info->view[0], info->wnd, &disrect);
+ debug_view_set_bounds(&info->view[1], info->wnd, &regrect);
+ debug_view_set_bounds(&info->view[2], info->wnd, &conrect);
+ smart_set_window_bounds(info->editwnd, info->wnd, &editrect);
+}
+
+
+
+//============================================================
+// console_process_string
+//============================================================
+
+static void console_process_string(debugwin_info *info, const char *string)
+{
+ TCHAR buffer = 0;
+
+ // an empty string is a single step
+ if (string[0] == 0)
+ debug_cpu_single_step(1);
+
+ // otherwise, just process the command
+ else
+ debug_console_execute_command(string, 1);
+
+ // clear the edit text box
+ SendMessage(info->editwnd, WM_SETTEXT, 0, (LPARAM)&buffer);
+}
+
+
+
+//============================================================
+// console_set_cpunum
+//============================================================
+
+static void console_set_cpunum(int cpunum)
+{
+ char title[256], curtitle[256];
+
+ // first set all the views to the new cpu number
+ if (main_console->view[0].view)
+ debug_view_set_property_UINT32(main_console->view[0].view, DVP_DASM_CPUNUM, cpunum);
+ if (main_console->view[1].view)
+ debug_view_set_property_UINT32(main_console->view[1].view, DVP_REGS_CPUNUM, cpunum);
+
+ // then update the caption
+ snprintf(title, ARRAY_LENGTH(title), "Debug: %s - CPU %d (%s)", Machine->gamedrv->name, cpu_getactivecpu(), activecpu_name());
+ win_get_window_text_utf8(main_console->wnd, curtitle, sizeof(curtitle) / sizeof(curtitle[0]));
+ if (strcmp(title, curtitle))
+ win_set_window_text_utf8(main_console->wnd, title);
+}
+
+
+
+//============================================================
+// create_standard_menubar
+//============================================================
+
+static HMENU create_standard_menubar(void)
+{
+ HMENU menubar, debugmenu;
+
+ // create the debug menu
+ debugmenu = CreatePopupMenu();
+ if (debugmenu == NULL)
+ return NULL;
+ AppendMenu(debugmenu, MF_ENABLED, ID_NEW_MEMORY_WND, TEXT("New Memory Window\tCtrl+M"));
+ AppendMenu(debugmenu, MF_ENABLED, ID_NEW_DISASM_WND, TEXT("New Disassembly Window\tCtrl+D"));
+ AppendMenu(debugmenu, MF_ENABLED, ID_NEW_LOG_WND, TEXT("New Error Log Window\tCtrl+L"));
+ AppendMenu(debugmenu, MF_DISABLED | MF_SEPARATOR, 0, TEXT(""));
+ AppendMenu(debugmenu, MF_ENABLED, ID_RUN, TEXT("Run\tF5"));
+ AppendMenu(debugmenu, MF_ENABLED, ID_RUN_AND_HIDE, TEXT("Run and Hide Debugger\tF12"));
+ AppendMenu(debugmenu, MF_ENABLED, ID_NEXT_CPU, TEXT("Run to Next CPU\tF6"));
+ AppendMenu(debugmenu, MF_ENABLED, ID_RUN_IRQ, TEXT("Run until Next Interrupt on This CPU\tF7"));
+ AppendMenu(debugmenu, MF_ENABLED, ID_RUN_VBLANK, TEXT("Run until Next VBLANK\tF8"));
+ AppendMenu(debugmenu, MF_DISABLED | MF_SEPARATOR, 0, TEXT(""));
+ AppendMenu(debugmenu, MF_ENABLED, ID_STEP, TEXT("Step Into\tF11"));
+ AppendMenu(debugmenu, MF_ENABLED, ID_STEP_OVER, TEXT("Step Over\tF10"));
+ AppendMenu(debugmenu, MF_ENABLED, ID_STEP_OUT, TEXT("Step Out\tShift+F11"));
+ AppendMenu(debugmenu, MF_DISABLED | MF_SEPARATOR, 0, TEXT(""));
+ AppendMenu(debugmenu, MF_ENABLED, ID_SOFT_RESET, TEXT("Soft Reset\tF3"));
+ AppendMenu(debugmenu, MF_ENABLED, ID_HARD_RESET, TEXT("Hard Reset\tShift+F3"));
+ AppendMenu(debugmenu, MF_ENABLED, ID_EXIT, TEXT("Exit"));
+
+ // create the menu bar
+ menubar = CreateMenu();
+ if (menubar == NULL)
+ {
+ DestroyMenu(debugmenu);
+ return NULL;
+ }
+ AppendMenu(menubar, MF_ENABLED | MF_POPUP, (UINT_PTR)debugmenu, TEXT("Debug"));
+
+ return menubar;
+}
+
+
+
+//============================================================
+// global_handle_command
+//============================================================
+
+static int global_handle_command(debugwin_info *info, WPARAM wparam, LPARAM lparam)
+{
+ if (HIWORD(wparam) == 0)
+ switch (LOWORD(wparam))
+ {
+ case ID_NEW_MEMORY_WND:
+ memory_create_window();
+ return 1;
+
+ case ID_NEW_DISASM_WND:
+ disasm_create_window();
+ return 1;
+
+ case ID_NEW_LOG_WND:
+ log_create_window();
+ return 1;
+
+ case ID_RUN_AND_HIDE:
+ smart_show_all(FALSE);
+ case ID_RUN:
+ debug_cpu_go(~0);
+ return 1;
+
+ case ID_NEXT_CPU:
+ debug_cpu_next_cpu();
+ return 1;
+
+ case ID_RUN_VBLANK:
+ debug_cpu_go_vblank();
+ return 1;
+
+ case ID_RUN_IRQ:
+ debug_cpu_go_interrupt(-1);
+ return 1;
+
+ case ID_STEP:
+ debug_cpu_single_step(1);
+ return 1;
+
+ case ID_STEP_OVER:
+ debug_cpu_single_step_over(1);
+ return 1;
+
+ case ID_STEP_OUT:
+ debug_cpu_single_step_out();
+ return 1;
+
+ case ID_HARD_RESET:
+ mame_schedule_hard_reset(Machine);
+ return 1;
+
+ case ID_SOFT_RESET:
+ mame_schedule_soft_reset(Machine);
+ debug_cpu_go(~0);
+ return 1;
+
+ case ID_EXIT:
+ mame_schedule_exit(Machine);
+ return 1;
+ }
+
+ return 0;
+}
+
+
+
+//============================================================
+// global_handle_key
+//============================================================
+
+static int global_handle_key(debugwin_info *info, WPARAM wparam, LPARAM lparam)
+{
+ int ignoreme;
+
+ /* ignore any keys that are received while the debug key is down */
+ ignoreme = input_ui_pressed(IPT_UI_DEBUG_BREAK);
+ if (ignoreme)
+ return 1;
+
+ switch (wparam)
+ {
+ case VK_F3:
+ if (GetAsyncKeyState(VK_SHIFT) & 0x8000)
+ SendMessage(info->wnd, WM_COMMAND, ID_HARD_RESET, 0);
+ else
+ SendMessage(info->wnd, WM_COMMAND, ID_SOFT_RESET, 0);
+ return 1;
+
+ case VK_F4:
+ if (GetAsyncKeyState(VK_MENU) & 0x8000)
+ {
+ /* ajg - never gets here since 'alt' seems to be captured somewhere else - menu maybe? */
+ SendMessage(info->wnd, WM_COMMAND, ID_EXIT, 0);
+ return 1;
+ }
+ break;
+
+ case VK_F5:
+ SendMessage(info->wnd, WM_COMMAND, ID_RUN, 0);
+ return 1;
+
+ case VK_F6:
+ SendMessage(info->wnd, WM_COMMAND, ID_NEXT_CPU, 0);
+ return 1;
+
+ case VK_F7:
+ SendMessage(info->wnd, WM_COMMAND, ID_RUN_IRQ, 0);
+ return 1;
+
+ case VK_F8:
+ SendMessage(info->wnd, WM_COMMAND, ID_RUN_VBLANK, 0);
+ return 1;
+
+ case VK_F10:
+ SendMessage(info->wnd, WM_COMMAND, ID_STEP_OVER, 0);
+ return 1;
+
+ case VK_F11:
+ if (GetAsyncKeyState(VK_SHIFT) & 0x8000)
+ SendMessage(info->wnd, WM_COMMAND, ID_STEP_OUT, 0);
+ else
+ SendMessage(info->wnd, WM_COMMAND, ID_STEP, 0);
+ return 1;
+
+ case VK_F12:
+ SendMessage(info->wnd, WM_COMMAND, ID_RUN_AND_HIDE, 0);
+ return 1;
+
+ case 'M':
+ if (GetAsyncKeyState(VK_CONTROL) & 0x8000)
+ {
+ SendMessage(info->wnd, WM_COMMAND, ID_NEW_MEMORY_WND, 0);
+ return 1;
+ }
+ break;
+
+ case 'D':
+ if (GetAsyncKeyState(VK_CONTROL) & 0x8000)
+ {
+ SendMessage(info->wnd, WM_COMMAND, ID_NEW_DISASM_WND, 0);
+ return 1;
+ }
+ break;
+
+ case 'L':
+ if (GetAsyncKeyState(VK_CONTROL) & 0x8000)
+ {
+ SendMessage(info->wnd, WM_COMMAND, ID_NEW_LOG_WND, 0);
+ return 1;
+ }
+ break;
+ }
+
+ return 0;
+}
+
+
+
+//============================================================
+// smart_set_window_bounds
+//============================================================
+
+static void smart_set_window_bounds(HWND wnd, HWND parent, RECT *bounds)
+{
+ RECT curbounds;
+ int flags = 0;
+
+ // first get the current bounds, relative to the parent
+ GetWindowRect(wnd, &curbounds);
+ if (parent != NULL)
+ {
+ RECT parentbounds;
+ GetWindowRect(parent, &parentbounds);
+ curbounds.top -= parentbounds.top;
+ curbounds.bottom -= parentbounds.top;
+ curbounds.left -= parentbounds.left;
+ curbounds.right -= parentbounds.left;
+ }
+
+ // if the position matches, don't change it
+ if (curbounds.top == bounds->top && curbounds.left == bounds->left)
+ flags |= SWP_NOMOVE;
+ if ((curbounds.bottom - curbounds.top) == (bounds->bottom - bounds->top) &&
+ (curbounds.right - curbounds.left) == (bounds->right - bounds->left))
+ flags |= SWP_NOSIZE;
+
+ // if we need to, reposition the window
+ if (flags != (SWP_NOMOVE | SWP_NOSIZE))
+ SetWindowPos(wnd, NULL,
+ bounds->left, bounds->top,
+ bounds->right - bounds->left, bounds->bottom - bounds->top,
+ SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_NOZORDER | flags);
+}
+
+
+
+//============================================================
+// smart_show_window
+//============================================================
+
+static void smart_show_window(HWND wnd, BOOL show)
+{
+ BOOL visible = IsWindowVisible(wnd);
+ if ((visible && !show) || (!visible && show))
+ ShowWindow(wnd, show ? SW_SHOW : SW_HIDE);
+}
+
+
+
+//============================================================
+// smart_show_all
+//============================================================
+
+static void smart_show_all(BOOL show)
+{
+ debugwin_info *info;
+ if (!show)
+ SetForegroundWindow(win_window_list->hwnd);
+ for (info = window_list; info; info = info->next)
+ smart_show_window(info->wnd, show);
+}
diff --git a/src/osd/windows/debugwin.h b/src/osd/windows/debugwin.h
new file mode 100644
index 00000000000..7033f3b772e
--- /dev/null
+++ b/src/osd/windows/debugwin.h
@@ -0,0 +1,32 @@
+//============================================================
+//
+// debugwin.h - Win32 debug window handling
+//
+// Copyright (c) 1996-2007, Nicola Salmoria and the MAME Team.
+// Visit http://mamedev.org for licensing and usage restrictions.
+//
+//============================================================
+
+#ifndef __WIN_DEBUGWIN__
+#define __WIN_DEBUGWIN__
+
+
+//============================================================
+// GLOBAL VARIABLES
+//============================================================
+
+// windows
+
+
+
+//============================================================
+// PROTOTYPES
+//============================================================
+
+void debugwin_init_windows(void);
+void debugwin_destroy_windows(void);
+void debugwin_show(int type);
+void debugwin_update_during_game(void);
+int debugwin_is_debugger_visible(void);
+
+#endif
diff --git a/src/osd/windows/drawd3d.c b/src/osd/windows/drawd3d.c
new file mode 100644
index 00000000000..e819bf7f0e2
--- /dev/null
+++ b/src/osd/windows/drawd3d.c
@@ -0,0 +1,2421 @@
+//============================================================
+//
+// drawd3d.c - Win32 Direct3D implementation
+//
+// Copyright (c) 1996-2007, Nicola Salmoria and the MAME Team.
+// Visit http://mamedev.org for licensing and usage restrictions.
+//
+//============================================================
+
+// Useful info:
+// Windows XP/2003 shipped with DirectX 8.1
+// Windows 2000 shipped with DirectX 7a
+// Windows 98SE shipped with DirectX 6.1a
+// Windows 98 shipped with DirectX 5
+// Windows NT shipped with DirectX 3.0a
+// Windows 95 shipped with DirectX 2
+
+// standard windows headers
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#include <tchar.h>
+#include <mmsystem.h>
+#include <d3d9.h>
+
+// standard C headers
+#include <math.h>
+
+// MAME headers
+#include "render.h"
+#include "rendutil.h"
+#include "options.h"
+
+// MAMEOS headers
+#include "d3dintf.h"
+#include "winmain.h"
+#include "window.h"
+#include "config.h"
+#include "strconv.h"
+
+
+
+//============================================================
+// DEBUGGING
+//============================================================
+
+extern void mtlog_add(const char *event);
+
+
+
+//============================================================
+// CONSTANTS
+//============================================================
+
+#define ENABLE_BORDER_PIX (1)
+
+#define VERTEX_FORMAT (D3DFVF_XYZRHW | D3DFVF_DIFFUSE | D3DFVF_TEX1)
+#define VERTEX_BUFFER_SIZE (2048*4)
+
+enum
+{
+ TEXTURE_TYPE_PLAIN,
+ TEXTURE_TYPE_DYNAMIC,
+ TEXTURE_TYPE_SURFACE
+};
+
+
+
+//============================================================
+// MACROS
+//============================================================
+
+#define FSWAP(var1, var2) do { float temp = var1; var1 = var2; var2 = temp; } while (0)
+
+
+
+//============================================================
+// TYPE DEFINITIONS
+//============================================================
+
+/* texture_info holds information about a texture */
+typedef struct _texture_info texture_info;
+struct _texture_info
+{
+ texture_info * next; // next texture in the list
+ UINT32 hash; // hash value for the texture
+ UINT32 flags; // rendering flags
+ render_texinfo texinfo; // copy of the texture info
+ float ustart, ustop; // beginning/ending U coordinates
+ float vstart, vstop; // beginning/ending V coordinates
+ int rawwidth, rawheight; // raw width/height of the texture
+ int type; // what type of texture are we?
+ int borderpix; // do we have a 1 pixel border?
+ int xprescale; // what is our X prescale factor?
+ int yprescale; // what is our Y prescale factor?
+ d3d_texture * d3dtex; // Direct3D texture pointer
+ d3d_surface * d3dsurface; // Direct3D offscreen plain surface pointer
+ d3d_texture * d3dfinaltex; // Direct3D final (post-scaled) texture
+};
+
+
+/* poly_info holds information about a single polygon/d3d primitive */
+typedef struct _poly_info poly_info;
+struct _poly_info
+{
+ D3DPRIMITIVETYPE type; // type of primitive
+ UINT32 count; // total number of primitives
+ UINT32 numverts; // total number of vertices
+ UINT32 flags; // rendering flags
+ DWORD modmode; // texture modulation mode
+ texture_info * texture; // pointer to texture info
+};
+
+
+/* d3d_vertex describes a single vertex */
+typedef struct _d3d_vertex d3d_vertex;
+struct _d3d_vertex
+{
+ float x, y, z; // X,Y,Z coordinates
+ float rhw; // 1/W coordinate
+ D3DCOLOR color; // diffuse color
+ float u0, v0; // texture stage 0 coordinates
+};
+
+
+/* d3d_info is the information about Direct3D for the current screen */
+typedef struct _d3d_info d3d_info;
+struct _d3d_info
+{
+ int adapter; // ordinal adapter number
+ int width, height; // current width, height
+ int refresh; // current refresh rate
+
+ d3d_device * device; // pointer to the Direct3DDevice object
+ int gamma_supported; // is full screen gamma supported?
+ d3d_present_parameters presentation; // set of presentation parameters
+ D3DDISPLAYMODE origmode; // original display mode for the adapter
+ D3DFORMAT pixformat; // pixel format we are using
+
+ d3d_vertex_buffer * vertexbuf; // pointer to the vertex buffer object
+ d3d_vertex * lockedbuf; // pointer to the locked vertex buffer
+ int numverts; // number of accumulated vertices
+
+ poly_info poly[VERTEX_BUFFER_SIZE / 3];// array to hold polygons as they are created
+ int numpolys; // number of accumulated polygons
+
+ texture_info * texlist; // list of active textures
+ int dynamic_supported; // are dynamic textures supported?
+ int stretch_supported; // is StretchRect with point filtering supported?
+ int mod2x_supported; // is D3DTOP_MODULATE2X supported?
+ int mod4x_supported; // is D3DTOP_MODULATE4X supported?
+ D3DFORMAT screen_format; // format to use for screen textures
+ D3DFORMAT yuv_format; // format to use for YUV textures
+
+ DWORD texture_caps; // textureCaps field
+ DWORD texture_max_aspect; // texture maximum aspect ratio
+ DWORD texture_max_width; // texture maximum width
+ DWORD texture_max_height; // texture maximum height
+
+ texture_info * last_texture; // previous texture
+ int last_blendenable; // previous blendmode
+ int last_blendop; // previous blendmode
+ int last_blendsrc; // previous blendmode
+ int last_blenddst; // previous blendmode
+ int last_filter; // previous texture filter
+ int last_wrap; // previous wrap state
+ DWORD last_modmode; // previous texture modulation
+
+ mame_bitmap * vector_bitmap; // experimental: bitmap for vectors
+ texture_info * vector_texture; // experimental: texture for vectors
+};
+
+
+/* line_aa_step is used for drawing antialiased lines */
+typedef struct _line_aa_step line_aa_step;
+struct _line_aa_step
+{
+ float xoffs, yoffs; // X/Y deltas
+ float weight; // weight contribution
+};
+
+
+
+//============================================================
+// GLOBALS
+//============================================================
+
+static d3d * d3dintf;
+
+static const line_aa_step line_aa_1step[] =
+{
+ { 0.00f, 0.00f, 1.00f },
+ { 0 }
+};
+
+static const line_aa_step line_aa_4step[] =
+{
+ { -0.25f, 0.00f, 0.25f },
+ { 0.25f, 0.00f, 0.25f },
+ { 0.00f, -0.25f, 0.25f },
+ { 0.00f, 0.25f, 0.25f },
+ { 0 }
+};
+
+
+
+//============================================================
+// INLINES
+//============================================================
+
+INLINE UINT32 ycc_to_rgb(UINT8 y, UINT8 cb, UINT8 cr)
+{
+ /* original equations:
+
+ C = Y - 16
+ D = Cb - 128
+ E = Cr - 128
+
+ R = clip(( 298 * C + 409 * E + 128) >> 8)
+ G = clip(( 298 * C - 100 * D - 208 * E + 128) >> 8)
+ B = clip(( 298 * C + 516 * D + 128) >> 8)
+
+ R = clip(( 298 * (Y - 16) + 409 * (Cr - 128) + 128) >> 8)
+ G = clip(( 298 * (Y - 16) - 100 * (Cb - 128) - 208 * (Cr - 128) + 128) >> 8)
+ B = clip(( 298 * (Y - 16) + 516 * (Cb - 128) + 128) >> 8)
+
+ R = clip(( 298 * Y - 298 * 16 + 409 * Cr - 409 * 128 + 128) >> 8)
+ G = clip(( 298 * Y - 298 * 16 - 100 * Cb + 100 * 128 - 208 * Cr + 208 * 128 + 128) >> 8)
+ B = clip(( 298 * Y - 298 * 16 + 516 * Cb - 516 * 128 + 128) >> 8)
+
+ R = clip(( 298 * Y - 298 * 16 + 409 * Cr - 409 * 128 + 128) >> 8)
+ G = clip(( 298 * Y - 298 * 16 - 100 * Cb + 100 * 128 - 208 * Cr + 208 * 128 + 128) >> 8)
+ B = clip(( 298 * Y - 298 * 16 + 516 * Cb - 516 * 128 + 128) >> 8)
+ */
+ int r, g, b, common;
+
+ common = 298 * y - 298 * 16;
+ r = (common + 409 * cr - 409 * 128 + 128) >> 8;
+ g = (common - 100 * cb + 100 * 128 - 208 * cr + 208 * 128 + 128) >> 8;
+ b = (common + 516 * cb - 516 * 128 + 128) >> 8;
+
+ if (r < 0) r = 0;
+ else if (r > 255) r = 255;
+ if (g < 0) g = 0;
+ else if (g > 255) g = 255;
+ if (b < 0) b = 0;
+ else if (b > 255) b = 255;
+
+ return MAKE_ARGB(0xff, r, g, b);
+}
+
+
+INLINE UINT32 texture_compute_hash(const render_texinfo *texture, UINT32 flags)
+{
+ return (FPTR)texture->base ^ (flags & (PRIMFLAG_BLENDMODE_MASK | PRIMFLAG_TEXFORMAT_MASK));
+}
+
+
+INLINE void set_texture(d3d_info *d3d, texture_info *texture)
+{
+ HRESULT result;
+ if (texture != d3d->last_texture)
+ {
+ d3d->last_texture = texture;
+ result = (*d3dintf->device.set_texture)(d3d->device, 0, (texture == NULL) ? NULL : texture->d3dfinaltex);
+ if (result != D3D_OK) mame_printf_verbose("Direct3D: Error %08X during device set_texture call\n", (int)result);
+ }
+}
+
+
+INLINE void set_filter(d3d_info *d3d, int filter)
+{
+ HRESULT result;
+ if (filter != d3d->last_filter)
+ {
+ d3d->last_filter = filter;
+ result = (*d3dintf->device.set_texture_stage_state)(d3d->device, 0, D3DTSS_MINFILTER, filter ? D3DTEXF_LINEAR : D3DTEXF_POINT);
+ if (result != D3D_OK) mame_printf_verbose("Direct3D: Error %08X during device set_texture_stage_state call\n", (int)result);
+ result = (*d3dintf->device.set_texture_stage_state)(d3d->device, 0, D3DTSS_MAGFILTER, filter ? D3DTEXF_LINEAR : D3DTEXF_POINT);
+ if (result != D3D_OK) mame_printf_verbose("Direct3D: Error %08X during device set_texture_stage_state call\n", (int)result);
+ }
+}
+
+
+INLINE void set_wrap(d3d_info *d3d, int wrap)
+{
+ HRESULT result;
+ if (wrap != d3d->last_wrap)
+ {
+ d3d->last_wrap = wrap;
+ result = (*d3dintf->device.set_texture_stage_state)(d3d->device, 0, D3DTSS_ADDRESSU, wrap ? D3DTADDRESS_WRAP : D3DTADDRESS_CLAMP);
+ if (result != D3D_OK) mame_printf_verbose("Direct3D: Error %08X during device set_texture_stage_state call\n", (int)result);
+ result = (*d3dintf->device.set_texture_stage_state)(d3d->device, 0, D3DTSS_ADDRESSV, wrap ? D3DTADDRESS_WRAP : D3DTADDRESS_CLAMP);
+ if (result != D3D_OK) mame_printf_verbose("Direct3D: Error %08X during device set_texture_stage_state call\n", (int)result);
+ }
+}
+
+
+INLINE void set_modmode(d3d_info *d3d, DWORD modmode)
+{
+ HRESULT result;
+ if (modmode != d3d->last_modmode)
+ {
+ d3d->last_modmode = modmode;
+ result = (*d3dintf->device.set_texture_stage_state)(d3d->device, 0, D3DTSS_COLOROP, modmode);
+ if (result != D3D_OK) mame_printf_verbose("Direct3D: Error %08X during device set_texture_stage_state call\n", (int)result);
+ }
+}
+
+
+INLINE void set_blendmode(d3d_info *d3d, int blendmode)
+{
+ HRESULT result;
+ int blendenable;
+ int blendop;
+ int blendsrc;
+ int blenddst;
+
+ // choose the parameters
+ switch (blendmode)
+ {
+ default:
+ case BLENDMODE_NONE: blendenable = FALSE; blendop = D3DBLENDOP_ADD; blendsrc = D3DBLEND_SRCALPHA; blenddst = D3DBLEND_INVSRCALPHA; break;
+ case BLENDMODE_ALPHA: blendenable = TRUE; blendop = D3DBLENDOP_ADD; blendsrc = D3DBLEND_SRCALPHA; blenddst = D3DBLEND_INVSRCALPHA; break;
+ case BLENDMODE_RGB_MULTIPLY: blendenable = TRUE; blendop = D3DBLENDOP_ADD; blendsrc = D3DBLEND_DESTCOLOR; blenddst = D3DBLEND_ZERO; break;
+ case BLENDMODE_ADD: blendenable = TRUE; blendop = D3DBLENDOP_ADD; blendsrc = D3DBLEND_SRCALPHA; blenddst = D3DBLEND_ONE; break;
+ }
+
+ // adjust the bits that changed
+ if (blendenable != d3d->last_blendenable)
+ {
+ d3d->last_blendenable = blendenable;
+ result = (*d3dintf->device.set_render_state)(d3d->device, D3DRS_ALPHABLENDENABLE, blendenable);
+ if (result != D3D_OK) mame_printf_verbose("Direct3D: Error %08X during device set_render_state call\n", (int)result);
+ }
+
+ if (blendop != d3d->last_blendop)
+ {
+ d3d->last_blendop = blendop;
+ result = (*d3dintf->device.set_render_state)(d3d->device, D3DRS_BLENDOP, blendop);
+ if (result != D3D_OK) mame_printf_verbose("Direct3D: Error %08X during device set_render_state call\n", (int)result);
+ }
+
+ if (blendsrc != d3d->last_blendsrc)
+ {
+ d3d->last_blendsrc = blendsrc;
+ result = (*d3dintf->device.set_render_state)(d3d->device, D3DRS_SRCBLEND, blendsrc);
+ if (result != D3D_OK) mame_printf_verbose("Direct3D: Error %08X during device set_render_state call\n", (int)result);
+ }
+
+ if (blenddst != d3d->last_blenddst)
+ {
+ d3d->last_blenddst = blenddst;
+ result = (*d3dintf->device.set_render_state)(d3d->device, D3DRS_DESTBLEND, blenddst);
+ if (result != D3D_OK) mame_printf_verbose("Direct3D: Error %08X during device set_render_state call\n", (int)result);
+ }
+}
+
+
+INLINE void reset_render_states(d3d_info *d3d)
+{
+ // this ensures subsequent calls to the above setters will force-update the data
+ d3d->last_texture = (texture_info *)~0;
+ d3d->last_filter = -1;
+ d3d->last_blendenable = -1;
+ d3d->last_blendop = -1;
+ d3d->last_blendsrc = -1;
+ d3d->last_blenddst = -1;
+ d3d->last_wrap = -1;
+}
+
+
+
+//============================================================
+// PROTOTYPES
+//============================================================
+
+// core functions
+static void drawd3d_exit(void);
+static int drawd3d_window_init(win_window_info *window);
+static void drawd3d_window_destroy(win_window_info *window);
+static const render_primitive_list *drawd3d_window_get_primitives(win_window_info *window);
+static int drawd3d_window_draw(win_window_info *window, HDC dc, int update);
+
+// devices
+static int device_create(win_window_info *window);
+static int device_create_resources(d3d_info *d3d);
+static void device_delete(d3d_info *d3d);
+static void device_delete_resources(d3d_info *d3d);
+static int device_verify_caps(d3d_info *d3d);
+static int device_test_cooperative(d3d_info *d3d);
+
+// video modes
+static int config_adapter_mode(win_window_info *window);
+static int get_adapter_for_monitor(d3d_info *d3d, win_monitor_info *monitor);
+static void pick_best_mode(win_window_info *window);
+static int update_window_size(win_window_info *window);
+
+// drawing
+static void draw_line(d3d_info *d3d, const render_primitive *prim);
+static void draw_quad(d3d_info *d3d, const render_primitive *prim);
+
+// primitives
+static d3d_vertex *primitive_alloc(d3d_info *d3d, int numverts);
+static void primitive_flush_pending(d3d_info *d3d);
+
+// textures
+static texture_info *texture_create(d3d_info *d3d, const render_texinfo *texsource, UINT32 flags);
+static void texture_compute_size(d3d_info *d3d, int texwidth, int texheight, texture_info *texture);
+static void texture_set_data(d3d_info *d3d, texture_info *texture, const render_texinfo *texsource, UINT32 flags);
+static void texture_prescale(d3d_info *d3d, texture_info *texture);
+static texture_info *texture_find(d3d_info *d3d, const render_primitive *prim);
+static void texture_update(d3d_info *d3d, const render_primitive *prim);
+
+
+
+//============================================================
+// drawd3d_init
+//============================================================
+
+int drawd3d_init(win_draw_callbacks *callbacks)
+{
+ int version = options_get_int(mame_options(), WINOPTION_D3DVERSION);
+ d3dintf = NULL;
+
+ // try Direct3D 9 if requested
+ if (version >= 9)
+ d3dintf = drawd3d9_init();
+
+ // if that didn't work, try Direct3D 8
+ if (d3dintf == NULL && version >= 8)
+ d3dintf = drawd3d8_init();
+
+ // if we failed, note the error
+ if (d3dintf == NULL)
+ {
+ mame_printf_error("Unable to initialize Direct3D.\n");
+ return 1;
+ }
+
+ // fill in the callbacks
+ callbacks->exit = drawd3d_exit;
+ callbacks->window_init = drawd3d_window_init;
+ callbacks->window_get_primitives = drawd3d_window_get_primitives;
+ callbacks->window_draw = drawd3d_window_draw;
+ callbacks->window_destroy = drawd3d_window_destroy;
+ return 0;
+}
+
+
+
+//============================================================
+// drawd3d_exit
+//============================================================
+
+static void drawd3d_exit(void)
+{
+ if (d3dintf != NULL)
+ (*d3dintf->d3d.release)(d3dintf);
+}
+
+
+
+//============================================================
+// drawd3d_window_init
+//============================================================
+
+static int drawd3d_window_init(win_window_info *window)
+{
+ d3d_info *d3d;
+
+ // allocate memory for our structures
+ d3d = malloc_or_die(sizeof(*d3d));
+ memset(d3d, 0, sizeof(*d3d));
+ window->drawdata = d3d;
+
+ // experimental: load a PNG to use for vector rendering; it is treated
+ // as a brightness map
+ d3d->vector_bitmap = render_load_png(NULL, "vector.png", NULL, NULL);
+ if (d3d->vector_bitmap != NULL)
+ {
+ fillbitmap(d3d->vector_bitmap, MAKE_ARGB(0xff,0xff,0xff,0xff), NULL);
+ d3d->vector_bitmap = render_load_png(NULL, "vector.png", d3d->vector_bitmap, NULL);
+ }
+
+ // configure the adapter for the mode we want
+ if (config_adapter_mode(window))
+ goto error;
+
+ // create the device immediately for the full screen case (defer for window mode)
+ if (window->fullscreen && device_create(window))
+ goto error;
+
+ return 0;
+
+error:
+ drawd3d_window_destroy(window);
+ mame_printf_error("Unable to initialize Direct3D.\n");
+ return 1;
+}
+
+
+
+//============================================================
+// drawd3d_window_destroy
+//============================================================
+
+static void drawd3d_window_destroy(win_window_info *window)
+{
+ d3d_info *d3d = window->drawdata;
+
+ // skip if nothing
+ if (d3d == NULL)
+ return;
+
+ // delete the device
+ device_delete(d3d);
+
+ // experimental: free the vector PNG
+ if (d3d->vector_bitmap != NULL)
+ bitmap_free(d3d->vector_bitmap);
+
+ // free the memory in the window
+ free(d3d);
+ window->drawdata = NULL;
+}
+
+
+
+//============================================================
+// drawd3d_window_get_primitives
+//============================================================
+
+static const render_primitive_list *drawd3d_window_get_primitives(win_window_info *window)
+{
+ d3d_info *d3d = window->drawdata;
+ RECT client;
+
+ GetClientRect(window->hwnd, &client);
+ if (rect_width(&client) > 0 && rect_height(&client) > 0)
+ {
+ render_target_set_bounds(window->target, rect_width(&client), rect_height(&client), winvideo_monitor_get_aspect(window->monitor));
+ render_target_set_max_update_rate(window->target, (d3d->refresh == 0) ? d3d->origmode.RefreshRate : d3d->refresh);
+ }
+ return render_target_get_primitives(window->target);
+}
+
+
+
+//============================================================
+// drawd3d_window_draw
+//============================================================
+
+static int drawd3d_window_draw(win_window_info *window, HDC dc, int update)
+{
+ d3d_info *d3d = window->drawdata;
+ const render_primitive *prim;
+ HRESULT result;
+
+ // if we haven't been created, just punt
+ if (d3d == NULL)
+ return 1;
+
+ // if we have a device, check the cooperative level
+ if (d3d->device != NULL)
+ {
+ int error = device_test_cooperative(d3d);
+ if (error)
+ return 1;
+ }
+
+ // in window mode, we need to track the window size
+ if (!window->fullscreen || d3d->device == NULL)
+ {
+ // if the size changes, skip this update since the render target will be out of date
+ if (update_window_size(window))
+ return 0;
+
+ // if we have no device, after updating the size, return an error so GDI can try
+ if (d3d->device == NULL)
+ return 1;
+ }
+
+mtlog_add("drawd3d_window_draw: begin");
+
+ // first update any textures
+ osd_lock_acquire(window->primlist->lock);
+ for (prim = window->primlist->head; prim != NULL; prim = prim->next)
+ if (prim->texture.base != NULL)
+ texture_update(d3d, prim);
+
+ // begin the scene
+mtlog_add("drawd3d_window_draw: begin_scene");
+ result = (*d3dintf->device.begin_scene)(d3d->device);
+ if (result != D3D_OK) mame_printf_verbose("Direct3D: Error %08X during device begin_scene call\n", (int)result);
+
+ d3d->lockedbuf = NULL;
+
+ // loop over primitives
+mtlog_add("drawd3d_window_draw: primitive loop begin");
+ for (prim = window->primlist->head; prim != NULL; prim = prim->next)
+ switch (prim->type)
+ {
+ case RENDER_PRIMITIVE_LINE:
+ draw_line(d3d, prim);
+ break;
+
+ case RENDER_PRIMITIVE_QUAD:
+ draw_quad(d3d, prim);
+ break;
+ }
+mtlog_add("drawd3d_window_draw: primitive loop end");
+ osd_lock_release(window->primlist->lock);
+
+ // flush any pending polygons
+mtlog_add("drawd3d_window_draw: flush_pending begin");
+ primitive_flush_pending(d3d);
+mtlog_add("drawd3d_window_draw: flush_pending end");
+
+ // finish the scene
+mtlog_add("drawd3d_window_draw: end_scene begin");
+ result = (*d3dintf->device.end_scene)(d3d->device);
+mtlog_add("drawd3d_window_draw: end_scene end");
+ if (result != D3D_OK) mame_printf_verbose("Direct3D: Error %08X during device end_scene call\n", (int)result);
+
+ // present the current buffers
+mtlog_add("drawd3d_window_draw: present begin");
+ result = (*d3dintf->device.present)(d3d->device, NULL, NULL, NULL, NULL, 0);
+mtlog_add("drawd3d_window_draw: present end");
+ if (result != D3D_OK) mame_printf_verbose("Direct3D: Error %08X during device present call\n", (int)result);
+ return 0;
+}
+
+
+
+//============================================================
+// device_create
+//============================================================
+
+static int device_create(win_window_info *window)
+{
+ d3d_info *d3d = window->drawdata;
+ HRESULT result;
+ int verify;
+
+ // if a device exists, free it
+ if (d3d->device != NULL)
+ device_delete(d3d);
+
+ // verify the caps
+ verify = device_verify_caps(d3d);
+ if (verify == 2)
+ {
+ mame_printf_error("Error: Device does not meet minimum requirements for Direct3D rendering\n");
+ return 1;
+ }
+ if (verify == 1)
+ mame_printf_warning("Warning: Device may not perform well for Direct3D rendering\n");
+
+ // verify texture formats
+ result = (*d3dintf->d3d.check_device_format)(d3dintf, d3d->adapter, D3DDEVTYPE_HAL, d3d->pixformat, 0, D3DRTYPE_TEXTURE, D3DFMT_A8R8G8B8);
+ if (result != D3D_OK)
+ {
+ mame_printf_error("Error: A8R8G8B8 format textures not supported\n");
+ return 1;
+ }
+
+ // pick a YUV texture format
+ d3d->yuv_format = D3DFMT_UYVY;
+ result = (*d3dintf->d3d.check_device_format)(d3dintf, d3d->adapter, D3DDEVTYPE_HAL, d3d->pixformat, 0, D3DRTYPE_TEXTURE, D3DFMT_UYVY);
+ if (result != D3D_OK)
+ {
+ d3d->yuv_format = D3DFMT_YUY2;
+ result = (*d3dintf->d3d.check_device_format)(d3dintf, d3d->adapter, D3DDEVTYPE_HAL, d3d->pixformat, 0, D3DRTYPE_TEXTURE, D3DFMT_YUY2);
+ if (result != D3D_OK)
+ d3d->yuv_format = D3DFMT_A8R8G8B8;
+ }
+ mame_printf_verbose("Direct3D: YUV format = %s\n", (d3d->yuv_format == D3DFMT_YUY2) ? "YUY2" : (d3d->yuv_format == D3DFMT_UYVY) ? "UYVY" : "RGB");
+
+try_again:
+ // try for XRGB first
+ d3d->screen_format = D3DFMT_X8R8G8B8;
+ result = (*d3dintf->d3d.check_device_format)(d3dintf, d3d->adapter, D3DDEVTYPE_HAL, d3d->pixformat, d3d->dynamic_supported ? D3DUSAGE_DYNAMIC : 0, D3DRTYPE_TEXTURE, d3d->screen_format);
+ if (result != D3D_OK)
+ {
+ // if not, try for ARGB
+ d3d->screen_format = D3DFMT_A8R8G8B8;
+ result = (*d3dintf->d3d.check_device_format)(d3dintf, d3d->adapter, D3DDEVTYPE_HAL, d3d->pixformat, d3d->dynamic_supported ? D3DUSAGE_DYNAMIC : 0, D3DRTYPE_TEXTURE, d3d->screen_format);
+ if (result != D3D_OK && d3d->dynamic_supported)
+ {
+ d3d->dynamic_supported = FALSE;
+ goto try_again;
+ }
+ if (result != D3D_OK)
+ {
+ mame_printf_error("Error: unable to configure a screen texture format\n");
+ return 1;
+ }
+ }
+
+ // initialize the D3D presentation parameters
+ memset(&d3d->presentation, 0, sizeof(d3d->presentation));
+ d3d->presentation.BackBufferWidth = d3d->width;
+ d3d->presentation.BackBufferHeight = d3d->height;
+ d3d->presentation.BackBufferFormat = d3d->pixformat;
+ d3d->presentation.BackBufferCount = video_config.triplebuf ? 2 : 1;
+ d3d->presentation.MultiSampleType = D3DMULTISAMPLE_NONE;
+ d3d->presentation.SwapEffect = D3DSWAPEFFECT_DISCARD;
+ d3d->presentation.hDeviceWindow = window->hwnd;
+ d3d->presentation.Windowed = !window->fullscreen || HAS_WINDOW_MENU;
+ d3d->presentation.EnableAutoDepthStencil = FALSE;
+ d3d->presentation.AutoDepthStencilFormat = D3DFMT_D16;
+ d3d->presentation.Flags = 0;
+ d3d->presentation.FullScreen_RefreshRateInHz = d3d->refresh;
+ d3d->presentation.PresentationInterval = ((video_config.triplebuf && window->fullscreen) || video_config.waitvsync || video_config.syncrefresh) ?
+ D3DPRESENT_INTERVAL_ONE : D3DPRESENT_INTERVAL_IMMEDIATE;
+ // create the D3D device
+ result = (*d3dintf->d3d.create_device)(d3dintf, d3d->adapter, D3DDEVTYPE_HAL, win_window_list->hwnd,
+ D3DCREATE_SOFTWARE_VERTEXPROCESSING | D3DCREATE_FPU_PRESERVE, &d3d->presentation, &d3d->device);
+ if (result != D3D_OK)
+ {
+ mame_printf_error("Unable to create the Direct3D device (%08X)\n", (UINT32)result);
+ return 1;
+ }
+ mame_printf_verbose("Direct3D: Device created at %dx%d\n", d3d->width, d3d->height);
+
+ // set the max texture size
+ render_target_set_max_texture_size(window->target, d3d->texture_max_width, d3d->texture_max_height);
+
+ // set the gamma if we need to
+ if (window->fullscreen)
+ {
+ // only set the gamma if it's not 1.0f
+ float brightness = options_get_float(mame_options(), WINOPTION_FULLSCREENBRIGHTNESS);
+ float contrast = options_get_float(mame_options(), WINOPTION_FULLLSCREENCONTRAST);
+ float gamma = options_get_float(mame_options(), WINOPTION_FULLSCREENGAMMA);
+ if (brightness != 1.0f || contrast != 1.0f || gamma != 1.0f)
+ {
+ // warn if we can't do it
+ if (!d3d->gamma_supported)
+ mame_printf_warning("Direct3D: Warning - device does not support full screen gamma correction.\n");
+ else
+ {
+ D3DGAMMARAMP ramp;
+ int i;
+
+ // create a standard ramp and set it
+ for (i = 0; i < 256; i++)
+ ramp.red[i] = ramp.green[i] = ramp.blue[i] = apply_brightness_contrast_gamma(i, brightness, contrast, gamma) << 8;
+ (*d3dintf->device.set_gamma_ramp)(d3d->device, 0, &ramp);
+ }
+ }
+ }
+
+ return device_create_resources(d3d);
+}
+
+
+
+//============================================================
+// device_create_resources
+//============================================================
+
+static int device_create_resources(d3d_info *d3d)
+{
+ HRESULT result;
+
+ // allocate a vertex buffer to use
+ result = (*d3dintf->device.create_vertex_buffer)(d3d->device,
+ sizeof(d3d_vertex) * VERTEX_BUFFER_SIZE,
+ D3DUSAGE_DYNAMIC | D3DUSAGE_SOFTWAREPROCESSING | D3DUSAGE_WRITEONLY,
+ VERTEX_FORMAT, D3DPOOL_DEFAULT, &d3d->vertexbuf);
+ if (result != D3D_OK)
+ {
+ mame_printf_error("Error creating vertex buffer (%08X)", (UINT32)result);
+ return 1;
+ }
+
+ // set the vertex format
+ result = (*d3dintf->device.set_vertex_shader)(d3d->device, VERTEX_FORMAT);
+ if (result != D3D_OK)
+ {
+ mame_printf_error("Error setting vertex shader (%08X)", (UINT32)result);
+ return 1;
+ }
+
+ // set the fixed render state
+ result = (*d3dintf->device.set_render_state)(d3d->device, D3DRS_ZENABLE, D3DZB_FALSE);
+ result = (*d3dintf->device.set_render_state)(d3d->device, D3DRS_FILLMODE, D3DFILL_SOLID);
+ result = (*d3dintf->device.set_render_state)(d3d->device, D3DRS_SHADEMODE, D3DSHADE_FLAT);
+ result = (*d3dintf->device.set_render_state)(d3d->device, D3DRS_ZWRITEENABLE, FALSE);
+ result = (*d3dintf->device.set_render_state)(d3d->device, D3DRS_ALPHATESTENABLE, TRUE);
+ result = (*d3dintf->device.set_render_state)(d3d->device, D3DRS_LASTPIXEL, TRUE);
+ result = (*d3dintf->device.set_render_state)(d3d->device, D3DRS_CULLMODE, D3DCULL_NONE);
+ result = (*d3dintf->device.set_render_state)(d3d->device, D3DRS_ZFUNC, D3DCMP_LESS);
+ result = (*d3dintf->device.set_render_state)(d3d->device, D3DRS_ALPHAREF, 0);
+ result = (*d3dintf->device.set_render_state)(d3d->device, D3DRS_ALPHAFUNC, D3DCMP_GREATER);
+ result = (*d3dintf->device.set_render_state)(d3d->device, D3DRS_DITHERENABLE, FALSE);
+ result = (*d3dintf->device.set_render_state)(d3d->device, D3DRS_FOGENABLE, FALSE);
+ result = (*d3dintf->device.set_render_state)(d3d->device, D3DRS_SPECULARENABLE, FALSE);
+ result = (*d3dintf->device.set_render_state)(d3d->device, D3DRS_STENCILENABLE, FALSE);
+ result = (*d3dintf->device.set_render_state)(d3d->device, D3DRS_WRAP0, FALSE);
+ result = (*d3dintf->device.set_render_state)(d3d->device, D3DRS_CLIPPING, TRUE);
+ result = (*d3dintf->device.set_render_state)(d3d->device, D3DRS_LIGHTING, FALSE);
+ result = (*d3dintf->device.set_render_state)(d3d->device, D3DRS_COLORVERTEX, TRUE);
+
+ result = (*d3dintf->device.set_texture_stage_state)(d3d->device, 0, D3DTSS_COLOROP, D3DTOP_MODULATE);
+ result = (*d3dintf->device.set_texture_stage_state)(d3d->device, 0, D3DTSS_ALPHAOP, D3DTOP_MODULATE);
+
+ // reset the local states to force updates
+ reset_render_states(d3d);
+
+ // clear the buffer
+ result = (*d3dintf->device.clear)(d3d->device, 0, NULL, D3DCLEAR_TARGET, D3DCOLOR_ARGB(0,0,0,0), 0, 0);
+ result = (*d3dintf->device.present)(d3d->device, NULL, NULL, NULL, NULL, 0);
+
+ // experimental: if we have a vector bitmap, create a texture for it
+ if (d3d->vector_bitmap != NULL)
+ {
+ render_texinfo texture;
+
+ // fake in the basic data so it looks like it came from render.c
+ texture.base = d3d->vector_bitmap->base;
+ texture.rowpixels = d3d->vector_bitmap->rowpixels;
+ texture.width = d3d->vector_bitmap->width;
+ texture.height = d3d->vector_bitmap->height;
+ texture.palette = NULL;
+ texture.seqid = 0;
+
+ // now create it
+ d3d->vector_texture = texture_create(d3d, &texture, PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA) | PRIMFLAG_TEXFORMAT(TEXFORMAT_ARGB32));
+ }
+
+ return 0;
+}
+
+
+
+//============================================================
+// device_delete
+//============================================================
+
+static void device_delete(d3d_info *d3d)
+{
+ // free resources
+ device_delete_resources(d3d);
+
+ // free the device itself
+ if (d3d->device != NULL)
+ (*d3dintf->device.release)(d3d->device);
+ d3d->device = NULL;
+}
+
+
+
+//============================================================
+// device_delete_resources
+//============================================================
+
+static void device_delete_resources(d3d_info *d3d)
+{
+ // free all textures
+ while (d3d->texlist != NULL)
+ {
+ texture_info *tex = d3d->texlist;
+ d3d->texlist = tex->next;
+ if (tex->d3dfinaltex != NULL)
+ (*d3dintf->texture.release)(tex->d3dfinaltex);
+ if (tex->d3dtex != NULL && tex->d3dtex != tex->d3dfinaltex)
+ (*d3dintf->texture.release)(tex->d3dtex);
+ if (tex->d3dsurface != NULL)
+ (*d3dintf->surface.release)(tex->d3dsurface);
+ free(tex);
+ }
+
+ // free the vertex buffer
+ if (d3d->vertexbuf != NULL)
+ (*d3dintf->vertexbuf.release)(d3d->vertexbuf);
+ d3d->vertexbuf = NULL;
+}
+
+
+
+//============================================================
+// device_verify_caps
+//============================================================
+
+static int device_verify_caps(d3d_info *d3d)
+{
+ int retval = 0;
+ HRESULT result;
+ DWORD tempcaps;
+
+ // fetch a few core caps
+ result = (*d3dintf->d3d.get_caps_dword)(d3dintf, d3d->adapter, D3DDEVTYPE_HAL, CAPS_TEXTURE_CAPS, &d3d->texture_caps);
+ if (result != D3D_OK) mame_printf_verbose("Direct3D: Error %08X during get_caps_dword call\n", (int)result);
+ result = (*d3dintf->d3d.get_caps_dword)(d3dintf, d3d->adapter, D3DDEVTYPE_HAL, CAPS_MAX_TEXTURE_ASPECT, &d3d->texture_max_aspect);
+ if (result != D3D_OK) mame_printf_verbose("Direct3D: Error %08X during get_caps_dword call\n", (int)result);
+ result = (*d3dintf->d3d.get_caps_dword)(d3dintf, d3d->adapter, D3DDEVTYPE_HAL, CAPS_MAX_TEXTURE_WIDTH, &d3d->texture_max_width);
+ if (result != D3D_OK) mame_printf_verbose("Direct3D: Error %08X during get_caps_dword call\n", (int)result);
+ result = (*d3dintf->d3d.get_caps_dword)(d3dintf, d3d->adapter, D3DDEVTYPE_HAL, CAPS_MAX_TEXTURE_HEIGHT, &d3d->texture_max_height);
+ if (result != D3D_OK) mame_printf_verbose("Direct3D: Error %08X during get_caps_dword call\n", (int)result);
+
+ // verify presentation capabilities
+ result = (*d3dintf->d3d.get_caps_dword)(d3dintf, d3d->adapter, D3DDEVTYPE_HAL, CAPS_PRESENTATION_INTERVALS, &tempcaps);
+ if (result != D3D_OK) mame_printf_verbose("Direct3D: Error %08X during get_caps_dword call\n", (int)result);
+ if (!(tempcaps & D3DPRESENT_INTERVAL_IMMEDIATE))
+ {
+ mame_printf_verbose("Direct3D: Error - Device does not support immediate presentations\n");
+ retval = 2;
+ }
+ if (!(tempcaps & D3DPRESENT_INTERVAL_ONE))
+ {
+ mame_printf_verbose("Direct3D: Error - Device does not support per-refresh presentations\n");
+ retval = 2;
+ }
+
+ // verify device capabilities
+ result = (*d3dintf->d3d.get_caps_dword)(d3dintf, d3d->adapter, D3DDEVTYPE_HAL, CAPS_DEV_CAPS, &tempcaps);
+ if (result != D3D_OK) mame_printf_verbose("Direct3D: Error %08X during get_caps_dword call\n", (int)result);
+ if (!(tempcaps & D3DDEVCAPS_CANRENDERAFTERFLIP))
+ {
+ mame_printf_verbose("Direct3D: Warning - Device does not support queued rendering after a page flip\n");
+ retval = 1;
+ }
+ if (!(tempcaps & D3DDEVCAPS_HWRASTERIZATION))
+ {
+ mame_printf_verbose("Direct3D: Warning - Device does not support hardware rasterization\n");
+ retval = 1;
+ }
+
+ // verify source blend capabilities
+ result = (*d3dintf->d3d.get_caps_dword)(d3dintf, d3d->adapter, D3DDEVTYPE_HAL, CAPS_SRCBLEND_CAPS, &tempcaps);
+ if (result != D3D_OK) mame_printf_verbose("Direct3D: Error %08X during get_caps_dword call\n", (int)result);
+ if (!(tempcaps & D3DPBLENDCAPS_SRCALPHA))
+ {
+ mame_printf_verbose("Direct3D: Error - Device does not support source alpha blending with source alpha\n");
+ retval = 2;
+ }
+ if (!(tempcaps & D3DPBLENDCAPS_DESTCOLOR))
+ {
+ mame_printf_verbose("Direct3D: Error - Device does not support source alpha blending with destination color\n");
+ retval = 2;
+ }
+
+ // verify destination blend capabilities
+ result = (*d3dintf->d3d.get_caps_dword)(d3dintf, d3d->adapter, D3DDEVTYPE_HAL, CAPS_DSTBLEND_CAPS, &tempcaps);
+ if (result != D3D_OK) mame_printf_verbose("Direct3D: Error %08X during get_caps_dword call\n", (int)result);
+ if (!(tempcaps & D3DPBLENDCAPS_ZERO))
+ {
+ mame_printf_verbose("Direct3D: Error - Device does not support dest alpha blending with zero\n");
+ retval = 2;
+ }
+ if (!(tempcaps & D3DPBLENDCAPS_ONE))
+ {
+ mame_printf_verbose("Direct3D: Error - Device does not support dest alpha blending with one\n");
+ retval = 2;
+ }
+ if (!(tempcaps & D3DPBLENDCAPS_INVSRCALPHA))
+ {
+ mame_printf_verbose("Direct3D: Error - Device does not support dest alpha blending with inverted source alpha\n");
+ retval = 2;
+ }
+
+ // verify texture capabilities
+ if (!(d3d->texture_caps & D3DPTEXTURECAPS_ALPHA))
+ {
+ mame_printf_verbose("Direct3D: Error - Device does not support texture alpha\n");
+ retval = 2;
+ }
+
+ // verify texture filter capabilities
+ result = (*d3dintf->d3d.get_caps_dword)(d3dintf, d3d->adapter, D3DDEVTYPE_HAL, CAPS_TEXTURE_FILTER_CAPS, &tempcaps);
+ if (result != D3D_OK) mame_printf_verbose("Direct3D: Error %08X during get_caps_dword call\n", (int)result);
+ if (!(tempcaps & D3DPTFILTERCAPS_MAGFPOINT))
+ {
+ mame_printf_verbose("Direct3D: Warning - Device does not support point-sample texture filtering for magnification\n");
+ retval = 1;
+ }
+ if (!(tempcaps & D3DPTFILTERCAPS_MAGFLINEAR))
+ {
+ mame_printf_verbose("Direct3D: Warning - Device does not support bilinear texture filtering for magnification\n");
+ retval = 1;
+ }
+ if (!(tempcaps & D3DPTFILTERCAPS_MINFPOINT))
+ {
+ mame_printf_verbose("Direct3D: Warning - Device does not support point-sample texture filtering for minification\n");
+ retval = 1;
+ }
+ if (!(tempcaps & D3DPTFILTERCAPS_MINFLINEAR))
+ {
+ mame_printf_verbose("Direct3D: Warning - Device does not support bilinear texture filtering for minification\n");
+ retval = 1;
+ }
+
+ // verify texture addressing capabilities
+ result = (*d3dintf->d3d.get_caps_dword)(d3dintf, d3d->adapter, D3DDEVTYPE_HAL, CAPS_TEXTURE_ADDRESS_CAPS, &tempcaps);
+ if (result != D3D_OK) mame_printf_verbose("Direct3D: Error %08X during get_caps_dword call\n", (int)result);
+ if (!(tempcaps & D3DPTADDRESSCAPS_CLAMP))
+ {
+ mame_printf_verbose("Direct3D: Warning - Device does not support texture clamping\n");
+ retval = 1;
+ }
+ if (!(tempcaps & D3DPTADDRESSCAPS_WRAP))
+ {
+ mame_printf_verbose("Direct3D: Warning - Device does not support texture wrapping\n");
+ retval = 1;
+ }
+
+ // verify texture operation capabilities
+ result = (*d3dintf->d3d.get_caps_dword)(d3dintf, d3d->adapter, D3DDEVTYPE_HAL, CAPS_TEXTURE_OP_CAPS, &tempcaps);
+ if (result != D3D_OK) mame_printf_verbose("Direct3D: Error %08X during get_caps_dword call\n", (int)result);
+ if (!(tempcaps & D3DTEXOPCAPS_MODULATE))
+ {
+ mame_printf_verbose("Direct3D: Warning - Device does not support texture modulation\n");
+ retval = 1;
+ }
+
+ // set a simpler flag to indicate mod2x and mod4x texture modes
+ d3d->mod2x_supported = ((tempcaps & D3DTEXOPCAPS_MODULATE2X) != 0);
+ d3d->mod4x_supported = ((tempcaps & D3DTEXOPCAPS_MODULATE4X) != 0);
+
+ // set a simpler flag to indicate we can use a gamma ramp
+ result = (*d3dintf->d3d.get_caps_dword)(d3dintf, d3d->adapter, D3DDEVTYPE_HAL, CAPS_CAPS2, &tempcaps);
+ if (result != D3D_OK) mame_printf_verbose("Direct3D: Error %08X during get_caps_dword call\n", (int)result);
+ d3d->dynamic_supported = ((tempcaps & D3DCAPS2_DYNAMICTEXTURES) != 0);
+ d3d->gamma_supported = ((tempcaps & D3DCAPS2_FULLSCREENGAMMA) != 0);
+ if (d3d->dynamic_supported) mame_printf_verbose("Direct3D: Using dynamic textures\n");
+
+ // set a simpler flag to indicate we can use StretchRect
+ result = (*d3dintf->d3d.get_caps_dword)(d3dintf, d3d->adapter, D3DDEVTYPE_HAL, CAPS_STRETCH_RECT_FILTER, &tempcaps);
+ if (result != D3D_OK) mame_printf_verbose("Direct3D: Error %08X during get_caps_dword call\n", (int)result);
+ d3d->stretch_supported = ((tempcaps & D3DPTFILTERCAPS_MAGFPOINT) != 0);
+ if (d3d->stretch_supported && video_config.prescale > 1) mame_printf_verbose("Direct3D: Using StretchRect for prescaling\n");
+
+ return retval;
+}
+
+
+
+//============================================================
+// device_test_cooperative
+//============================================================
+
+static int device_test_cooperative(d3d_info *d3d)
+{
+ HRESULT result;
+
+ // check our current status; if we lost the device, punt to GDI
+ result = (*d3dintf->device.test_cooperative_level)(d3d->device);
+ if (result == D3DERR_DEVICELOST)
+ return 1;
+
+ // if we're able to reset ourselves, try it
+ if (result == D3DERR_DEVICENOTRESET)
+ {
+ mame_printf_verbose("Direct3D: resetting device\n");
+
+ // free all existing resources and call reset on the device
+ device_delete_resources(d3d);
+ result = (*d3dintf->device.reset)(d3d->device, &d3d->presentation);
+
+ // if it didn't work, punt to GDI
+ if (result != D3D_OK)
+ return 1;
+
+ // try to create the resources again; if that didn't work, delete the whole thing
+ if (device_create_resources(d3d))
+ {
+ mame_printf_verbose("Direct3D: failed to recreate resources for device; failing permanently\n");
+ device_delete(d3d);
+ return 1;
+ }
+ }
+ return 0;
+}
+
+
+
+//============================================================
+// config_adapter_mode
+//============================================================
+
+static int config_adapter_mode(win_window_info *window)
+{
+ d3d_adapter_identifier identifier;
+ d3d_info *d3d = window->drawdata;
+ HRESULT result;
+
+ // choose the monitor number
+ d3d->adapter = get_adapter_for_monitor(d3d, window->monitor);
+
+ // get the identifier
+ result = (*d3dintf->d3d.get_adapter_identifier)(d3dintf, d3d->adapter, 0, &identifier);
+ if (result != D3D_OK)
+ {
+ mame_printf_error("Error getting identifier for adapter #%d\n", d3d->adapter);
+ return 1;
+ }
+ mame_printf_verbose("Direct3D: Configuring adapter #%d = %s\n", d3d->adapter, identifier.Description);
+
+ // get the current display mode
+ result = (*d3dintf->d3d.get_adapter_display_mode)(d3dintf, d3d->adapter, &d3d->origmode);
+ if (result != D3D_OK)
+ {
+ mame_printf_error("Error getting mode for adapter #%d\n", d3d->adapter);
+ return 1;
+ }
+
+ // choose a resolution: window mode case
+ if (!window->fullscreen || !video_config.switchres || HAS_WINDOW_MENU)
+ {
+ RECT client;
+
+ // bounds are from the window client rect
+ GetClientRect(window->hwnd, &client);
+ d3d->width = client.right - client.left;
+ d3d->height = client.bottom - client.top;
+
+ // pix format is from the current mode
+ d3d->pixformat = d3d->origmode.Format;
+ d3d->refresh = 0;
+
+ // make sure it's a pixel format we can get behind
+ if (d3d->pixformat != D3DFMT_X1R5G5B5 && d3d->pixformat != D3DFMT_R5G6B5 && d3d->pixformat != D3DFMT_X8R8G8B8)
+ {
+ char *utf8_device = utf8_from_tstring(window->monitor->info.szDevice);
+ if (utf8_device != NULL)
+ {
+ mame_printf_error("Device %s currently in an unsupported mode\n", utf8_device);
+ free(utf8_device);
+ }
+ return 1;
+ }
+ }
+
+ // choose a resolution: full screen mode case
+ else
+ {
+ // default to the current mode exactly
+ d3d->width = d3d->origmode.Width;
+ d3d->height = d3d->origmode.Height;
+ d3d->pixformat = d3d->origmode.Format;
+ d3d->refresh = d3d->origmode.RefreshRate;
+
+ // if we're allowed to switch resolutions, override with something better
+ if (video_config.switchres)
+ pick_best_mode(window);
+ }
+
+ // see if we can handle the device type
+ result = (*d3dintf->d3d.check_device_type)(d3dintf, d3d->adapter, D3DDEVTYPE_HAL, d3d->pixformat, d3d->pixformat, !window->fullscreen);
+ if (result != D3D_OK)
+ {
+ char *utf8_device = utf8_from_tstring(window->monitor->info.szDevice);
+ if (utf8_device != NULL)
+ {
+ mame_printf_error("Proposed video mode not supported on device %s\n", utf8_device);
+ free(utf8_device);
+ }
+ return 1;
+ }
+ return 0;
+}
+
+
+
+//============================================================
+// get_adapter_for_monitor
+//============================================================
+
+static int get_adapter_for_monitor(d3d_info *d3d, win_monitor_info *monitor)
+{
+ int maxadapter = (*d3dintf->d3d.get_adapter_count)(d3dintf);
+ int adapternum;
+
+ // iterate over adapters until we error or find a match
+ for (adapternum = 0; adapternum < maxadapter; adapternum++)
+ {
+ HMONITOR curmonitor;
+
+ // get the monitor for this adapter
+ curmonitor = (*d3dintf->d3d.get_adapter_monitor)(d3dintf, adapternum);
+
+ // if we match the proposed monitor, this is it
+ if (curmonitor == monitor->handle)
+ return adapternum;
+ }
+
+ // default to the default
+ return D3DADAPTER_DEFAULT;
+}
+
+
+
+//============================================================
+// pick_best_mode
+//============================================================
+
+static void pick_best_mode(win_window_info *window)
+{
+ double target_refresh = ATTOSECONDS_TO_HZ(Machine->screen[0].refresh);
+ INT32 target_width, target_height;
+ d3d_info *d3d = window->drawdata;
+ INT32 minwidth, minheight;
+ float best_score = 0.0;
+ int maxmodes;
+ int modenum;
+
+ // determine the minimum width/height for the selected target
+ // note: technically we should not be calling this from an alternate window
+ // thread; however, it is only done during init time, and the init code on
+ // the main thread is waiting for us to finish, so it is safe to do so here
+ render_target_get_minimum_size(window->target, &minwidth, &minheight);
+
+ // use those as the target for now
+ target_width = minwidth;
+ target_height = minheight;
+
+ // determine the maximum number of modes
+ maxmodes = (*d3dintf->d3d.get_adapter_mode_count)(d3dintf, d3d->adapter, D3DFMT_X8R8G8B8);
+
+ // enumerate all the video modes and find the best match
+ mame_printf_verbose("Direct3D: Selecting video mode...\n");
+ for (modenum = 0; modenum < maxmodes; modenum++)
+ {
+ float size_score, refresh_score, final_score;
+ D3DDISPLAYMODE mode;
+ HRESULT result;
+
+ // check this mode
+ result = (*d3dintf->d3d.enum_adapter_modes)(d3dintf, d3d->adapter, D3DFMT_X8R8G8B8, modenum, &mode);
+ if (result != D3D_OK)
+ break;
+
+ // skip non-32 bit modes
+ if (mode.Format != D3DFMT_X8R8G8B8)
+ continue;
+
+ // compute initial score based on difference between target and current
+ size_score = 1.0f / (1.0f + fabs(mode.Width - target_width) + fabs(mode.Height - target_height));
+
+ // if the mode is too small, give a big penalty
+ if (mode.Width < minwidth || mode.Height < minheight)
+ size_score *= 0.01f;
+
+ // if mode is smaller than we'd like, it only scores up to 0.1
+ if (mode.Width < target_width || mode.Height < target_height)
+ size_score *= 0.1f;
+
+ // if we're looking for a particular mode, that's a winner
+ if (mode.Width == window->maxwidth && mode.Height == window->maxheight)
+ size_score = 2.0f;
+
+ // compute refresh score
+ refresh_score = 1.0f / (1.0f + fabs((double)mode.RefreshRate - target_refresh));
+
+ // if refresh is smaller than we'd like, it only scores up to 0.1
+ if ((double)mode.RefreshRate < target_refresh)
+ refresh_score *= 0.1;
+
+ // if we're looking for a particular refresh, make sure it matches
+ if (mode.RefreshRate == window->refresh)
+ refresh_score = 2.0f;
+
+ // weight size and refresh equally
+ final_score = size_score + refresh_score;
+
+ // best so far?
+ mame_printf_verbose(" %4dx%4d@%3dHz -> %f\n", mode.Width, mode.Height, mode.RefreshRate, final_score * 1000.0f);
+ if (final_score > best_score)
+ {
+ best_score = final_score;
+ d3d->width = mode.Width;
+ d3d->height = mode.Height;
+ d3d->pixformat = mode.Format;
+ d3d->refresh = mode.RefreshRate;
+ }
+ }
+ mame_printf_verbose("Direct3D: Mode selected = %4dx%4d@%3dHz\n", d3d->width, d3d->height, d3d->refresh);
+}
+
+
+
+//============================================================
+// update_window_size
+//============================================================
+
+static int update_window_size(win_window_info *window)
+{
+ d3d_info *d3d = window->drawdata;
+ RECT client;
+
+ // get the current window bounds
+ GetClientRect(window->hwnd, &client);
+
+ // if we have a device and matching width/height, nothing to do
+ if (d3d->device != NULL && rect_width(&client) == d3d->width && rect_height(&client) == d3d->height)
+ {
+ // clear out any pending resizing if the area didn't change
+ if (window->resize_state == RESIZE_STATE_PENDING)
+ window->resize_state = RESIZE_STATE_NORMAL;
+ return FALSE;
+ }
+
+ // if we're in the middle of resizing, leave it alone as well
+ if (window->resize_state == RESIZE_STATE_RESIZING)
+ return FALSE;
+
+ // set the new bounds and create the device again
+ d3d->width = rect_width(&client);
+ d3d->height = rect_height(&client);
+ if (device_create(window))
+ return FALSE;
+
+ // reset the resize state to normal, and indicate we made a change
+ window->resize_state = RESIZE_STATE_NORMAL;
+ return TRUE;
+}
+
+
+
+//============================================================
+// draw_line
+//============================================================
+
+static void draw_line(d3d_info *d3d, const render_primitive *prim)
+{
+ const line_aa_step *step = line_aa_4step;
+ render_bounds b0, b1;
+ d3d_vertex *vertex;
+ INT32 r, g, b, a;
+ poly_info *poly;
+ float effwidth;
+ DWORD color;
+ int i;
+
+ // compute the effective width based on the direction of the line
+ effwidth = prim->width;
+ if (effwidth < 0.5f)
+ effwidth = 0.5f;
+
+ // determine the bounds of a quad to draw this line
+ render_line_to_quad(&prim->bounds, effwidth, &b0, &b1);
+
+ // iterate over AA steps
+ for (step = PRIMFLAG_GET_ANTIALIAS(prim->flags) ? line_aa_4step : line_aa_1step; step->weight != 0; step++)
+ {
+ // get a pointer to the vertex buffer
+ vertex = primitive_alloc(d3d, 4);
+ if (vertex == NULL)
+ return;
+
+ // rotate the unit vector by 135 degrees and add to point 0
+ vertex[0].x = b0.x0 + step->xoffs;
+ vertex[0].y = b0.y0 + step->yoffs;
+
+ // rotate the unit vector by -135 degrees and add to point 0
+ vertex[1].x = b0.x1 + step->xoffs;
+ vertex[1].y = b0.y1 + step->yoffs;
+
+ // rotate the unit vector by 45 degrees and add to point 1
+ vertex[2].x = b1.x0 + step->xoffs;
+ vertex[2].y = b1.y0 + step->yoffs;
+
+ // rotate the unit vector by -45 degrees and add to point 1
+ vertex[3].x = b1.x1 + step->xoffs;
+ vertex[3].y = b1.y1 + step->yoffs;
+
+ // determine the color of the line
+ r = (INT32)(prim->color.r * step->weight * 255.0f);
+ g = (INT32)(prim->color.g * step->weight * 255.0f);
+ b = (INT32)(prim->color.b * step->weight * 255.0f);
+ a = (INT32)(prim->color.a * 255.0f);
+ if (r > 255) r = 255;
+ if (g > 255) g = 255;
+ if (b > 255) b = 255;
+ if (a > 255) a = 255;
+ color = D3DCOLOR_ARGB(a, r, g, b);
+
+ // if we have a texture to use for the vectors, use it here
+ if (d3d->vector_texture != NULL)
+ {
+ vertex[0].u0 = d3d->vector_texture->ustart;
+ vertex[0].v0 = d3d->vector_texture->vstart;
+
+ vertex[2].u0 = d3d->vector_texture->ustop;
+ vertex[2].v0 = d3d->vector_texture->vstart;
+
+ vertex[1].u0 = d3d->vector_texture->ustart;
+ vertex[1].v0 = d3d->vector_texture->vstop;
+
+ vertex[3].u0 = d3d->vector_texture->ustop;
+ vertex[3].v0 = d3d->vector_texture->vstop;
+ }
+
+ // set the color, Z parameters to standard values
+ for (i = 0; i < 4; i++)
+ {
+ vertex[i].z = 0.0f;
+ vertex[i].rhw = 1.0f;
+ vertex[i].color = color;
+ }
+
+ // now add a polygon entry
+ poly = &d3d->poly[d3d->numpolys++];
+ poly->type = D3DPT_TRIANGLESTRIP;
+ poly->count = 2;
+ poly->numverts = 4;
+ poly->flags = prim->flags;
+ poly->modmode = D3DTOP_MODULATE;
+ poly->texture = d3d->vector_texture;
+ }
+}
+
+
+
+//============================================================
+// draw_quad
+//============================================================
+
+static void draw_quad(d3d_info *d3d, const render_primitive *prim)
+{
+ texture_info *texture = texture_find(d3d, prim);
+ DWORD color, modmode;
+ d3d_vertex *vertex;
+ INT32 r, g, b, a;
+ poly_info *poly;
+ int i;
+
+ // get a pointer to the vertex buffer
+ vertex = primitive_alloc(d3d, 4);
+ if (vertex == NULL)
+ return;
+
+ // fill in the vertexes clockwise
+ vertex[0].x = prim->bounds.x0 - 0.5f;
+ vertex[0].y = prim->bounds.y0 - 0.5f;
+ vertex[1].x = prim->bounds.x1 - 0.5f;
+ vertex[1].y = prim->bounds.y0 - 0.5f;
+ vertex[2].x = prim->bounds.x0 - 0.5f;
+ vertex[2].y = prim->bounds.y1 - 0.5f;
+ vertex[3].x = prim->bounds.x1 - 0.5f;
+ vertex[3].y = prim->bounds.y1 - 0.5f;
+
+ // set the texture coordinates
+ if (texture != NULL)
+ {
+ float du = texture->ustop - texture->ustart;
+ float dv = texture->vstop - texture->vstart;
+ vertex[0].u0 = texture->ustart + du * prim->texcoords.tl.u;
+ vertex[0].v0 = texture->vstart + dv * prim->texcoords.tl.v;
+ vertex[1].u0 = texture->ustart + du * prim->texcoords.tr.u;
+ vertex[1].v0 = texture->vstart + dv * prim->texcoords.tr.v;
+ vertex[2].u0 = texture->ustart + du * prim->texcoords.bl.u;
+ vertex[2].v0 = texture->vstart + dv * prim->texcoords.bl.v;
+ vertex[3].u0 = texture->ustart + du * prim->texcoords.br.u;
+ vertex[3].v0 = texture->vstart + dv * prim->texcoords.br.v;
+ }
+
+ // determine the color, allowing for over modulation
+ r = (INT32)(prim->color.r * 255.0f);
+ g = (INT32)(prim->color.g * 255.0f);
+ b = (INT32)(prim->color.b * 255.0f);
+ a = (INT32)(prim->color.a * 255.0f);
+ modmode = D3DTOP_MODULATE;
+ if (texture != NULL)
+ {
+ if (d3d->mod2x_supported && (r > 255 || g > 255 || b > 255))
+ {
+ if (d3d->mod4x_supported && (r > 2*255 || g > 2*255 || b > 2*255))
+ {
+ r >>= 2; g >>= 2; b >>= 2;
+ modmode = D3DTOP_MODULATE4X;
+ }
+ else
+ {
+ r >>= 1; g >>= 1; b >>= 1;
+ modmode = D3DTOP_MODULATE2X;
+ }
+ }
+ }
+ if (r > 255) r = 255;
+ if (g > 255) g = 255;
+ if (b > 255) b = 255;
+ if (a > 255) a = 255;
+ color = D3DCOLOR_ARGB(a, r, g, b);
+
+ // set the color, Z parameters to standard values
+ for (i = 0; i < 4; i++)
+ {
+ vertex[i].z = 0.0f;
+ vertex[i].rhw = 1.0f;
+ vertex[i].color = color;
+ }
+
+ // now add a polygon entry
+ poly = &d3d->poly[d3d->numpolys++];
+ poly->type = D3DPT_TRIANGLESTRIP;
+ poly->count = 2;
+ poly->numverts = 4;
+ poly->flags = prim->flags;
+ poly->modmode = modmode;
+ poly->texture = texture;
+}
+
+
+
+//============================================================
+// primitive_alloc
+//============================================================
+
+static d3d_vertex *primitive_alloc(d3d_info *d3d, int numverts)
+{
+ HRESULT result;
+
+ // if we're going to overflow, flush
+ if (d3d->lockedbuf != NULL && d3d->numverts + numverts >= VERTEX_BUFFER_SIZE)
+ primitive_flush_pending(d3d);
+
+ // if we don't have a lock, grab it now
+ if (d3d->lockedbuf == NULL)
+ {
+ result = (*d3dintf->vertexbuf.lock)(d3d->vertexbuf, 0, 0, (VOID **)&d3d->lockedbuf, D3DLOCK_DISCARD);
+ if (result != D3D_OK)
+ return NULL;
+ }
+
+ // if we already have the lock and enough room, just return a pointer
+ if (d3d->lockedbuf != NULL && d3d->numverts + numverts < VERTEX_BUFFER_SIZE)
+ {
+ int oldverts = d3d->numverts;
+ d3d->numverts += numverts;
+ return &d3d->lockedbuf[oldverts];
+ }
+ return NULL;
+}
+
+
+
+//============================================================
+// primitive_flush_pending
+//============================================================
+
+static void primitive_flush_pending(d3d_info *d3d)
+{
+ HRESULT result;
+ int polynum;
+ int vertnum;
+
+ // ignore if we're not locked
+ if (d3d->lockedbuf == NULL)
+ return;
+
+ // unlock the buffer
+ result = (*d3dintf->vertexbuf.unlock)(d3d->vertexbuf);
+ if (result != D3D_OK) mame_printf_verbose("Direct3D: Error %08X during vertex buffer unlock call\n", (int)result);
+ d3d->lockedbuf = NULL;
+
+ // set the stream
+ result = (*d3dintf->device.set_stream_source)(d3d->device, 0, d3d->vertexbuf, sizeof(d3d_vertex));
+ if (result != D3D_OK) mame_printf_verbose("Direct3D: Error %08X during device set_stream_source call\n", (int)result);
+
+ // now do the polys
+ for (polynum = vertnum = 0; polynum < d3d->numpolys; polynum++)
+ {
+ poly_info *poly = &d3d->poly[polynum];
+ int newfilter;
+
+ // set the texture if different
+ set_texture(d3d, poly->texture);
+
+ // set filtering if different
+ if (poly->texture != NULL)
+ {
+ newfilter = FALSE;
+ if (PRIMFLAG_GET_SCREENTEX(poly->flags))
+ newfilter = video_config.filter;
+ set_filter(d3d, newfilter);
+ set_wrap(d3d, PRIMFLAG_GET_TEXWRAP(poly->flags));
+ set_modmode(d3d, poly->modmode);
+ }
+
+ // set the blendmode if different
+ set_blendmode(d3d, PRIMFLAG_GET_BLENDMODE(poly->flags));
+
+ // add the primitives
+ assert(vertnum + poly->numverts <= d3d->numverts);
+ result = (*d3dintf->device.draw_primitive)(d3d->device, poly->type, vertnum, poly->count);
+ if (result != D3D_OK) mame_printf_verbose("Direct3D: Error %08X during device draw_primitive call\n", (int)result);
+ vertnum += poly->numverts;
+ }
+
+ // reset the vertex count
+ d3d->numverts = 0;
+ d3d->numpolys = 0;
+}
+
+
+
+//============================================================
+// texture_create
+//============================================================
+
+static texture_info *texture_create(d3d_info *d3d, const render_texinfo *texsource, UINT32 flags)
+{
+ texture_info *texture;
+ HRESULT result;
+
+ // allocate a new texture
+ texture = malloc_or_die(sizeof(*texture));
+ memset(texture, 0, sizeof(*texture));
+
+ // fill in the core data
+ texture->hash = texture_compute_hash(texsource, flags);
+ texture->flags = flags;
+ texture->texinfo = *texsource;
+ texture->xprescale = video_config.prescale;
+ texture->yprescale = video_config.prescale;
+
+ // compute the size
+ texture_compute_size(d3d, texsource->width, texsource->height, texture);
+
+ // non-screen textures are easy
+ if (!PRIMFLAG_GET_SCREENTEX(flags))
+ {
+ assert(PRIMFLAG_TEXFORMAT(flags) != TEXFORMAT_YUY16);
+ result = (*d3dintf->device.create_texture)(d3d->device, texture->rawwidth, texture->rawheight, 1, 0, D3DFMT_A8R8G8B8, D3DPOOL_MANAGED, &texture->d3dtex);
+ if (result != D3D_OK)
+ goto error;
+ texture->d3dfinaltex = texture->d3dtex;
+ texture->type = TEXTURE_TYPE_PLAIN;
+ }
+
+ // screen textures are allocated differently
+ else
+ {
+ D3DFORMAT format;
+ DWORD usage = d3d->dynamic_supported ? D3DUSAGE_DYNAMIC : 0;
+ DWORD pool = d3d->dynamic_supported ? D3DPOOL_DEFAULT : D3DPOOL_MANAGED;
+ int maxdim = MAX(d3d->presentation.BackBufferWidth, d3d->presentation.BackBufferHeight);
+
+ // pick the format
+ if (PRIMFLAG_GET_TEXFORMAT(flags) == TEXFORMAT_YUY16)
+ format = d3d->yuv_format;
+ else if (PRIMFLAG_GET_TEXFORMAT(flags) == TEXFORMAT_ARGB32 || PRIMFLAG_GET_TEXFORMAT(flags) == TEXFORMAT_PALETTEA16)
+ format = D3DFMT_A8R8G8B8;
+ else
+ format = d3d->screen_format;
+
+ // don't prescale above screen size
+ while (texture->xprescale > 1 && texture->rawwidth * texture->xprescale >= 2 * maxdim)
+ texture->xprescale--;
+ while (texture->xprescale > 1 && texture->rawwidth * texture->xprescale > d3d->texture_max_width)
+ texture->xprescale--;
+ while (texture->yprescale > 1 && texture->rawheight * texture->yprescale >= 2 * maxdim)
+ texture->yprescale--;
+ while (texture->yprescale > 1 && texture->rawheight * texture->yprescale > d3d->texture_max_height)
+ texture->yprescale--;
+ if (texture->xprescale != video_config.prescale || texture->yprescale != video_config.prescale)
+ mame_printf_verbose("Direct3D: adjusting prescale from %dx%d to %dx%d\n", video_config.prescale, video_config.prescale, texture->xprescale, texture->yprescale);
+
+ // screen textures with no prescaling are pretty easy
+ if (texture->xprescale == 1 && texture->yprescale == 1)
+ {
+ result = (*d3dintf->device.create_texture)(d3d->device, texture->rawwidth, texture->rawheight, 1, usage, format, pool, &texture->d3dtex);
+ if (result != D3D_OK)
+ goto error;
+ texture->d3dfinaltex = texture->d3dtex;
+ texture->type = d3d->dynamic_supported ? TEXTURE_TYPE_DYNAMIC : TEXTURE_TYPE_PLAIN;
+ }
+
+ // screen textures with prescaling require two allocations
+ else
+ {
+ int scwidth, scheight;
+
+ // use an offscreen plain surface for stretching if supported
+ if (d3d->stretch_supported)
+ {
+ result = (*d3dintf->device.create_offscreen_plain_surface)(d3d->device, texture->rawwidth, texture->rawheight, format, D3DPOOL_DEFAULT, &texture->d3dsurface);
+ if (result != D3D_OK)
+ goto error;
+ texture->type = TEXTURE_TYPE_SURFACE;
+ }
+
+ // otherwise, we allocate a dynamic texture for the source
+ else
+ {
+ result = (*d3dintf->device.create_texture)(d3d->device, texture->rawwidth, texture->rawheight, 1, usage, format, pool, &texture->d3dtex);
+ if (result != D3D_OK)
+ goto error;
+ texture->type = d3d->dynamic_supported ? TEXTURE_TYPE_DYNAMIC : TEXTURE_TYPE_PLAIN;
+ }
+
+ // for the target surface, we allocate a render target texture
+ scwidth = texture->rawwidth * texture->xprescale;
+ scheight = texture->rawheight * texture->yprescale;
+ result = (*d3dintf->device.create_texture)(d3d->device, scwidth, scheight, 1, D3DUSAGE_RENDERTARGET, format, D3DPOOL_DEFAULT, &texture->d3dfinaltex);
+ if (result != D3D_OK)
+ goto error;
+ }
+ }
+
+ // copy the data to the texture
+ texture_set_data(d3d, texture, texsource, flags);
+
+ // add us to the texture list
+ texture->next = d3d->texlist;
+ d3d->texlist = texture;
+ return texture;
+
+error:
+ if (texture->d3dsurface != NULL)
+ (*d3dintf->surface.release)(texture->d3dsurface);
+ if (texture->d3dtex != NULL)
+ (*d3dintf->texture.release)(texture->d3dtex);
+ free(texture);
+ return NULL;
+}
+
+
+
+//============================================================
+// texture_compute_size
+//============================================================
+
+static void texture_compute_size(d3d_info *d3d, int texwidth, int texheight, texture_info *texture)
+{
+ int finalheight = texheight;
+ int finalwidth = texwidth;
+
+ // if we're not wrapping, add a 1 pixel border on all sides
+#if ENABLE_BORDER_PIX
+ texture->borderpix = !(texture->flags & PRIMFLAG_TEXWRAP_MASK);
+#else
+ texture->borderpix = FALSE;
+#endif
+ if (texture->borderpix)
+ {
+ finalwidth += 2;
+ finalheight += 2;
+ }
+
+ // round width/height up to nearest power of 2 if we need to
+ if (!(d3d->texture_caps & D3DPTEXTURECAPS_NONPOW2CONDITIONAL))
+ {
+ // first the width
+ if (finalwidth & (finalwidth - 1))
+ {
+ finalwidth |= finalwidth >> 1;
+ finalwidth |= finalwidth >> 2;
+ finalwidth |= finalwidth >> 4;
+ finalwidth |= finalwidth >> 8;
+ finalwidth++;
+ }
+
+ // then the height
+ if (finalheight & (finalheight - 1))
+ {
+ finalheight |= finalheight >> 1;
+ finalheight |= finalheight >> 2;
+ finalheight |= finalheight >> 4;
+ finalheight |= finalheight >> 8;
+ finalheight++;
+ }
+ }
+
+ // round up to square if we need to
+ if (d3d->texture_caps & D3DPTEXTURECAPS_SQUAREONLY)
+ {
+ if (finalwidth < finalheight)
+ finalwidth = finalheight;
+ else
+ finalheight = finalwidth;
+ }
+
+ // adjust the aspect ratio if we need to
+ while (finalwidth < finalheight && finalheight / finalwidth > d3d->texture_max_aspect)
+ finalwidth *= 2;
+ while (finalheight < finalwidth && finalwidth / finalheight > d3d->texture_max_aspect)
+ finalheight *= 2;
+
+ // if we added pixels for the border, and that just barely pushed us over, take it back
+ if (texture->borderpix &&
+ ((finalwidth > d3d->texture_max_width && finalwidth - 2 <= d3d->texture_max_width) ||
+ (finalheight > d3d->texture_max_height && finalheight - 2 <= d3d->texture_max_height)))
+ {
+ texture->borderpix = FALSE;
+ finalwidth -= 2;
+ finalheight -= 2;
+ }
+
+ // if we're above the max width/height, do what?
+ if (finalwidth > d3d->texture_max_width || finalheight > d3d->texture_max_height)
+ {
+ static int printed = FALSE;
+ if (!printed) mame_printf_warning("Texture too big! (wanted: %dx%d, max is %dx%d)\n", finalwidth, finalheight, (int)d3d->texture_max_width, (int)d3d->texture_max_height);
+ printed = TRUE;
+ }
+
+ // compute the U/V scale factors
+ if (texture->borderpix)
+ {
+ texture->ustart = 1.0f / (float)finalwidth;
+ texture->ustop = (float)(texwidth + 1) / (float)finalwidth;
+ texture->vstart = 1.0f / (float)finalheight;
+ texture->vstop = (float)(texheight + 1) / (float)finalheight;
+ }
+ else
+ {
+ texture->ustart = 0.0f;
+ texture->ustop = (float)texwidth / (float)finalwidth;
+ texture->vstart = 0.0f;
+ texture->vstop = (float)texheight / (float)finalheight;
+ }
+
+ // set the final values
+ texture->rawwidth = finalwidth;
+ texture->rawheight = finalheight;
+}
+
+
+
+//============================================================
+// copyline_palette16
+//============================================================
+
+INLINE void copyline_palette16(UINT32 *dst, const UINT16 *src, int width, const rgb_t *palette, int borderpix)
+{
+ int x;
+
+ if (borderpix)
+ *dst++ = 0xff000000 | palette[*src];
+ for (x = 0; x < width; x++)
+ *dst++ = 0xff000000 | palette[*src++];
+ if (borderpix)
+ *dst++ = 0xff000000 | palette[*--src];
+}
+
+
+
+//============================================================
+// copyline_palettea16
+//============================================================
+
+INLINE void copyline_palettea16(UINT32 *dst, const UINT16 *src, int width, const rgb_t *palette, int borderpix)
+{
+ int x;
+
+ if (borderpix)
+ *dst++ = palette[*src];
+ for (x = 0; x < width; x++)
+ *dst++ = palette[*src++];
+ if (borderpix)
+ *dst++ = palette[*--src];
+}
+
+
+
+//============================================================
+// copyline_rgb15
+//============================================================
+
+INLINE void copyline_rgb15(UINT32 *dst, const UINT16 *src, int width, const rgb_t *palette, int borderpix)
+{
+ int x;
+
+ // palette (really RGB map) case
+ if (palette != NULL)
+ {
+ if (borderpix)
+ {
+ UINT16 pix = *src;
+ *dst++ = 0xff000000 | palette[0x40 + ((pix >> 10) & 0x1f)] | palette[0x20 + ((pix >> 5) & 0x1f)] | palette[0x00 + ((pix >> 0) & 0x1f)];
+ }
+ for (x = 0; x < width; x++)
+ {
+ UINT16 pix = *src++;
+ *dst++ = 0xff000000 | palette[0x40 + ((pix >> 10) & 0x1f)] | palette[0x20 + ((pix >> 5) & 0x1f)] | palette[0x00 + ((pix >> 0) & 0x1f)];
+ }
+ if (borderpix)
+ {
+ UINT16 pix = *--src;
+ *dst++ = 0xff000000 | palette[0x40 + ((pix >> 10) & 0x1f)] | palette[0x20 + ((pix >> 5) & 0x1f)] | palette[0x00 + ((pix >> 0) & 0x1f)];
+ }
+ }
+
+ // direct case
+ else
+ {
+ if (borderpix)
+ {
+ UINT16 pix = *src;
+ UINT32 color = ((pix & 0x7c00) << 9) | ((pix & 0x03e0) << 6) | ((pix & 0x001f) << 3);
+ *dst++ = 0xff000000 | color | ((color >> 5) & 0x070707);
+ }
+ for (x = 0; x < width; x++)
+ {
+ UINT16 pix = *src++;
+ UINT32 color = ((pix & 0x7c00) << 9) | ((pix & 0x03e0) << 6) | ((pix & 0x001f) << 3);
+ *dst++ = 0xff000000 | color | ((color >> 5) & 0x070707);
+ }
+ if (borderpix)
+ {
+ UINT16 pix = *--src;
+ UINT32 color = ((pix & 0x7c00) << 9) | ((pix & 0x03e0) << 6) | ((pix & 0x001f) << 3);
+ *dst++ = 0xff000000 | color | ((color >> 5) & 0x070707);
+ }
+ }
+}
+
+
+
+//============================================================
+// copyline_rgb32
+//============================================================
+
+INLINE void copyline_rgb32(UINT32 *dst, const UINT32 *src, int width, const rgb_t *palette, int borderpix)
+{
+ int x;
+
+ // palette (really RGB map) case
+ if (palette != NULL)
+ {
+ if (borderpix)
+ {
+ UINT32 srcpix = *src;
+ *dst++ = 0xff000000 | palette[0x200 + RGB_RED(srcpix)] | palette[0x100 + RGB_GREEN(srcpix)] | palette[RGB_BLUE(srcpix)];
+ }
+ for (x = 0; x < width; x++)
+ {
+ UINT32 srcpix = *src++;
+ *dst++ = 0xff000000 | palette[0x200 + RGB_RED(srcpix)] | palette[0x100 + RGB_GREEN(srcpix)] | palette[RGB_BLUE(srcpix)];
+ }
+ if (borderpix)
+ {
+ UINT32 srcpix = *--src;
+ *dst++ = 0xff000000 | palette[0x200 + RGB_RED(srcpix)] | palette[0x100 + RGB_GREEN(srcpix)] | palette[RGB_BLUE(srcpix)];
+ }
+ }
+
+ // direct case
+ else
+ {
+ if (borderpix)
+ *dst++ = 0xff000000 | *src;
+ for (x = 0; x < width; x++)
+ *dst++ = 0xff000000 | *src++;
+ if (borderpix)
+ *dst++ = 0xff000000 | *--src;
+ }
+}
+
+
+
+//============================================================
+// copyline_argb32
+//============================================================
+
+INLINE void copyline_argb32(UINT32 *dst, const UINT32 *src, int width, const rgb_t *palette, int borderpix)
+{
+ int x;
+
+ // palette (really RGB map) case
+ if (palette != NULL)
+ {
+ if (borderpix)
+ {
+ UINT32 srcpix = *src;
+ *dst++ = (srcpix & 0xff000000) | palette[0x200 + RGB_RED(srcpix)] | palette[0x100 + RGB_GREEN(srcpix)] | palette[RGB_BLUE(srcpix)];
+ }
+ for (x = 0; x < width; x++)
+ {
+ UINT32 srcpix = *src++;
+ *dst++ = (srcpix & 0xff000000) | palette[0x200 + RGB_RED(srcpix)] | palette[0x100 + RGB_GREEN(srcpix)] | palette[RGB_BLUE(srcpix)];
+ }
+ if (borderpix)
+ {
+ UINT32 srcpix = *--src;
+ *dst++ = (srcpix & 0xff000000) | palette[0x200 + RGB_RED(srcpix)] | palette[0x100 + RGB_GREEN(srcpix)] | palette[RGB_BLUE(srcpix)];
+ }
+ }
+
+ // direct case
+ else
+ {
+ if (borderpix)
+ *dst++ = *src;
+ for (x = 0; x < width; x++)
+ *dst++ = *src++;
+ if (borderpix)
+ *dst++ = *--src;
+ }
+}
+
+
+
+//============================================================
+// copyline_yuy16_to_yuy2
+//============================================================
+
+INLINE void copyline_yuy16_to_yuy2(UINT16 *dst, const UINT16 *src, int width, const rgb_t *palette, int borderpix)
+{
+ int x;
+
+ // palette (really RGB map) case
+ if (palette != NULL)
+ {
+ if (borderpix)
+ {
+ UINT16 srcpix = *src;
+ *dst++ = palette[0x000 + (srcpix >> 8)] | (srcpix << 8);
+ }
+ for (x = 0; x < width; x++)
+ {
+ UINT16 srcpix = *src++;
+ *dst++ = palette[0x000 + (srcpix >> 8)] | (srcpix << 8);
+ }
+ if (borderpix)
+ {
+ UINT16 srcpix = *--src;
+ *dst++ = palette[0x000 + (srcpix >> 8)] | (srcpix << 8);
+ }
+ }
+
+ // direct case
+ else
+ {
+ if (borderpix)
+ {
+ UINT16 srcpix = *src;
+ *dst++ = (srcpix >> 8) | (srcpix << 8);
+ }
+ for (x = 0; x < width; x++)
+ {
+ UINT16 srcpix = *src++;
+ *dst++ = (srcpix >> 8) | (srcpix << 8);
+ }
+ if (borderpix)
+ {
+ UINT16 srcpix = *--src;
+ *dst++ = (srcpix >> 8) | (srcpix << 8);
+ }
+ }
+}
+
+
+
+//============================================================
+// copyline_yuy16_to_uyvy
+//============================================================
+
+INLINE void copyline_yuy16_to_uyvy(UINT16 *dst, const UINT16 *src, int width, const rgb_t *palette, int borderpix)
+{
+ int x;
+
+ // palette (really RGB map) case
+ if (palette != NULL)
+ {
+ if (borderpix)
+ {
+ UINT16 srcpix = *src;
+ *dst++ = palette[0x100 + (srcpix >> 8)] | (srcpix & 0xff);
+ }
+ for (x = 0; x < width; x++)
+ {
+ UINT16 srcpix = *src++;
+ *dst++ = palette[0x100 + (srcpix >> 8)] | (srcpix & 0xff);
+ }
+ if (borderpix)
+ {
+ UINT16 srcpix = *--src;
+ *dst++ = palette[0x100 + (srcpix >> 8)] | (srcpix & 0xff);
+ }
+ }
+
+ // direct case
+ else
+ {
+ if (borderpix)
+ {
+ UINT16 srcpix = *src;
+ *dst++ = (srcpix >> 8) | (srcpix << 8);
+ }
+ for (x = 0; x < width; x++)
+ {
+ UINT16 srcpix = *src++;
+ *dst++ = (srcpix >> 8) | (srcpix << 8);
+ }
+ if (borderpix)
+ {
+ UINT16 srcpix = *--src;
+ *dst++ = (srcpix >> 8) | (srcpix << 8);
+ }
+ }
+}
+
+
+
+//============================================================
+// copyline_yuy16_to_argb
+//============================================================
+
+INLINE void copyline_yuy16_to_argb(UINT32 *dst, const UINT16 *src, int width, const rgb_t *palette, int borderpix)
+{
+ int x;
+
+ // palette (really RGB map) case
+ if (palette != NULL)
+ {
+ if (borderpix)
+ {
+ UINT16 srcpix0 = src[0];
+ UINT16 srcpix1 = src[1];
+ UINT8 cb = srcpix0 & 0xff;
+ UINT8 cr = srcpix1 & 0xff;
+ *dst++ = ycc_to_rgb(palette[0x000 + (srcpix0 >> 8)], cb, cr);
+ }
+ for (x = 0; x < width / 2; x++)
+ {
+ UINT16 srcpix0 = *src++;
+ UINT16 srcpix1 = *src++;
+ UINT8 cb = srcpix0 & 0xff;
+ UINT8 cr = srcpix1 & 0xff;
+ *dst++ = ycc_to_rgb(palette[0x000 + (srcpix0 >> 8)], cb, cr);
+ *dst++ = ycc_to_rgb(palette[0x000 + (srcpix1 >> 8)], cb, cr);
+ }
+ if (borderpix)
+ {
+ UINT16 srcpix1 = *--src;
+ UINT16 srcpix0 = *--src;
+ UINT8 cb = srcpix0 & 0xff;
+ UINT8 cr = srcpix1 & 0xff;
+ *dst++ = ycc_to_rgb(palette[0x000 + (srcpix1 >> 8)], cb, cr);
+ }
+ }
+
+ // direct case
+ else
+ {
+ if (borderpix)
+ {
+ UINT16 srcpix0 = src[0];
+ UINT16 srcpix1 = src[1];
+ UINT8 cb = srcpix0 & 0xff;
+ UINT8 cr = srcpix1 & 0xff;
+ *dst++ = ycc_to_rgb(srcpix0 >> 8, cb, cr);
+ }
+ for (x = 0; x < width / 2; x++)
+ {
+ UINT16 srcpix0 = *src++;
+ UINT16 srcpix1 = *src++;
+ UINT8 cb = srcpix0 & 0xff;
+ UINT8 cr = srcpix1 & 0xff;
+ *dst++ = ycc_to_rgb(srcpix0 >> 8, cb, cr);
+ *dst++ = ycc_to_rgb(srcpix1 >> 8, cb, cr);
+ }
+ if (borderpix)
+ {
+ UINT16 srcpix1 = *--src;
+ UINT16 srcpix0 = *--src;
+ UINT8 cb = srcpix0 & 0xff;
+ UINT8 cr = srcpix1 & 0xff;
+ *dst++ = ycc_to_rgb(srcpix1 >> 8, cb, cr);
+ }
+ }
+}
+
+
+
+//============================================================
+// texture_set_data
+//============================================================
+
+static void texture_set_data(d3d_info *d3d, texture_info *texture, const render_texinfo *texsource, UINT32 flags)
+{
+ D3DLOCKED_RECT rect;
+ HRESULT result;
+ int miny, maxy;
+ int dsty;
+
+ // lock the texture
+ switch (texture->type)
+ {
+ default:
+ case TEXTURE_TYPE_PLAIN: result = (*d3dintf->texture.lock_rect)(texture->d3dtex, 0, &rect, NULL, 0); break;
+ case TEXTURE_TYPE_DYNAMIC: result = (*d3dintf->texture.lock_rect)(texture->d3dtex, 0, &rect, NULL, D3DLOCK_DISCARD); break;
+ case TEXTURE_TYPE_SURFACE: result = (*d3dintf->surface.lock_rect)(texture->d3dsurface, &rect, NULL, D3DLOCK_DISCARD); break;
+ }
+ if (result != D3D_OK)
+ return;
+
+ // loop over Y
+ miny = 0 - texture->borderpix;
+ maxy = texsource->height + texture->borderpix;
+ for (dsty = miny; dsty < maxy; dsty++)
+ {
+ int srcy = (dsty < 0) ? 0 : (dsty >= texsource->height) ? texsource->height - 1 : dsty;
+ void *dst = (BYTE *)rect.pBits + (dsty + texture->borderpix) * rect.Pitch;
+
+ // switch off of the format and
+ switch (PRIMFLAG_GET_TEXFORMAT(flags))
+ {
+ case TEXFORMAT_PALETTE16:
+ copyline_palette16(dst, (UINT16 *)texsource->base + srcy * texsource->rowpixels, texsource->width, texsource->palette, texture->borderpix);
+ break;
+
+ case TEXFORMAT_PALETTEA16:
+ copyline_palettea16(dst, (UINT16 *)texsource->base + srcy * texsource->rowpixels, texsource->width, texsource->palette, texture->borderpix);
+ break;
+
+ case TEXFORMAT_RGB15:
+ copyline_rgb15(dst, (UINT16 *)texsource->base + srcy * texsource->rowpixels, texsource->width, texsource->palette, texture->borderpix);
+ break;
+
+ case TEXFORMAT_RGB32:
+ copyline_rgb32(dst, (UINT32 *)texsource->base + srcy * texsource->rowpixels, texsource->width, texsource->palette, texture->borderpix);
+ break;
+
+ case TEXFORMAT_ARGB32:
+ copyline_argb32(dst, (UINT32 *)texsource->base + srcy * texsource->rowpixels, texsource->width, texsource->palette, texture->borderpix);
+ break;
+
+ case TEXFORMAT_YUY16:
+ if (d3d->yuv_format == D3DFMT_YUY2)
+ copyline_yuy16_to_yuy2(dst, (UINT16 *)texsource->base + srcy * texsource->rowpixels, texsource->width, texsource->palette, texture->borderpix);
+ else if (d3d->yuv_format == D3DFMT_UYVY)
+ copyline_yuy16_to_uyvy(dst, (UINT16 *)texsource->base + srcy * texsource->rowpixels, texsource->width, texsource->palette, texture->borderpix);
+ else
+ copyline_yuy16_to_argb(dst, (UINT16 *)texsource->base + srcy * texsource->rowpixels, texsource->width, texsource->palette, texture->borderpix);
+ break;
+
+ default:
+ mame_printf_error("Unknown texture blendmode=%d format=%d\n", PRIMFLAG_GET_BLENDMODE(flags), PRIMFLAG_GET_TEXFORMAT(flags));
+ break;
+ }
+ }
+
+ // unlock
+ switch (texture->type)
+ {
+ default:
+ case TEXTURE_TYPE_PLAIN: result = (*d3dintf->texture.unlock_rect)(texture->d3dtex, 0); break;
+ case TEXTURE_TYPE_DYNAMIC: result = (*d3dintf->texture.unlock_rect)(texture->d3dtex, 0); break;
+ case TEXTURE_TYPE_SURFACE: result = (*d3dintf->surface.unlock_rect)(texture->d3dsurface); break;
+ }
+ if (result != D3D_OK) mame_printf_verbose("Direct3D: Error %08X during texture unlock_rect call\n", (int)result);
+
+ // prescale
+ texture_prescale(d3d, texture);
+}
+
+
+
+//============================================================
+// texture_prescale
+//============================================================
+
+static void texture_prescale(d3d_info *d3d, texture_info *texture)
+{
+ d3d_surface *surface;
+ HRESULT result;
+ int i;
+
+ // if we don't need to, just skip it
+ if (texture->d3dtex == texture->d3dfinaltex)
+ return;
+
+ // for all cases, we need to get the surface of the render target
+ result = (*d3dintf->texture.get_surface_level)(texture->d3dfinaltex, 0, &surface);
+ if (result != D3D_OK) mame_printf_verbose("Direct3D: Error %08X during texture get_surface_level call\n", (int)result);
+
+ // if we have an offscreen plain surface, we can just StretchRect to it
+ if (texture->type == TEXTURE_TYPE_SURFACE)
+ {
+ RECT source, dest;
+
+ assert(texture->d3dsurface != NULL);
+
+ // set the source bounds
+ source.left = source.top = 0;
+ source.right = texture->texinfo.width + 2 * texture->borderpix;
+ source.bottom = texture->texinfo.height + 2 * texture->borderpix;
+
+ // set the target bounds
+ dest = source;
+ dest.right *= texture->xprescale;
+ dest.bottom *= texture->yprescale;
+
+ // do the stretchrect
+ result = (*d3dintf->device.stretch_rect)(d3d->device, texture->d3dsurface, &source, surface, &dest, D3DTEXF_POINT);
+ if (result != D3D_OK) mame_printf_verbose("Direct3D: Error %08X during device stretct_rect call\n", (int)result);
+ }
+
+ // if we are using a texture render target, we need to do more preparations
+ else
+ {
+ d3d_surface *backbuffer;
+
+ assert(texture->d3dtex != NULL);
+
+ // first remember the original render target and set the new one
+ result = (*d3dintf->device.get_render_target)(d3d->device, 0, &backbuffer);
+ if (result != D3D_OK) mame_printf_verbose("Direct3D: Error %08X during device get_render_target call\n", (int)result);
+ result = (*d3dintf->device.set_render_target)(d3d->device, 0, surface);
+ if (result != D3D_OK) mame_printf_verbose("Direct3D: Error %08X during device set_render_target call\n", (int)result);
+ reset_render_states(d3d);
+
+ // start the scene
+ result = (*d3dintf->device.begin_scene)(d3d->device);
+ if (result != D3D_OK) mame_printf_verbose("Direct3D: Error %08X during device begin_scene call\n", (int)result);
+
+ // configure the rendering pipeline
+ set_filter(d3d, FALSE);
+ set_blendmode(d3d, BLENDMODE_NONE);
+ result = (*d3dintf->device.set_texture)(d3d->device, 0, texture->d3dtex);
+ if (result != D3D_OK) mame_printf_verbose("Direct3D: Error %08X during device set_texture call\n", (int)result);
+
+ // lock the vertex buffer
+ result = (*d3dintf->vertexbuf.lock)(d3d->vertexbuf, 0, 0, (VOID **)&d3d->lockedbuf, D3DLOCK_DISCARD);
+ if (result != D3D_OK) mame_printf_verbose("Direct3D: Error %08X during vertex buffer lock call\n", (int)result);
+
+ // configure the X/Y coordinates on the target surface
+ d3d->lockedbuf[0].x = -0.5f;
+ d3d->lockedbuf[0].y = -0.5f;
+ d3d->lockedbuf[1].x = (float)((texture->texinfo.width + 2 * texture->borderpix) * texture->xprescale) - 0.5f;
+ d3d->lockedbuf[1].y = -0.5f;
+ d3d->lockedbuf[2].x = -0.5f;
+ d3d->lockedbuf[2].y = (float)((texture->texinfo.height + 2 * texture->borderpix) * texture->yprescale) - 0.5f;
+ d3d->lockedbuf[3].x = (float)((texture->texinfo.width + 2 * texture->borderpix) * texture->xprescale) - 0.5f;
+ d3d->lockedbuf[3].y = (float)((texture->texinfo.height + 2 * texture->borderpix) * texture->yprescale) - 0.5f;
+
+ // configure the U/V coordintes on the source texture
+ d3d->lockedbuf[0].u0 = 0.0f;
+ d3d->lockedbuf[0].v0 = 0.0f;
+ d3d->lockedbuf[1].u0 = (float)(texture->texinfo.width + 2 * texture->borderpix) / (float)texture->rawwidth;
+ d3d->lockedbuf[1].v0 = 0.0f;
+ d3d->lockedbuf[2].u0 = 0.0f;
+ d3d->lockedbuf[2].v0 = (float)(texture->texinfo.height + 2 * texture->borderpix) / (float)texture->rawheight;
+ d3d->lockedbuf[3].u0 = (float)(texture->texinfo.width + 2 * texture->borderpix) / (float)texture->rawwidth;
+ d3d->lockedbuf[3].v0 = (float)(texture->texinfo.height + 2 * texture->borderpix) / (float)texture->rawheight;
+
+ // reset the remaining vertex parameters
+ for (i = 0; i < 4; i++)
+ {
+ d3d->lockedbuf[i].z = 0.0f;
+ d3d->lockedbuf[i].rhw = 1.0f;
+ d3d->lockedbuf[i].color = D3DCOLOR_ARGB(0xff,0xff,0xff,0xff);
+ }
+
+ // unlock the vertex buffer
+ result = (*d3dintf->vertexbuf.unlock)(d3d->vertexbuf);
+ if (result != D3D_OK) mame_printf_verbose("Direct3D: Error %08X during vertex buffer unlock call\n", (int)result);
+ d3d->lockedbuf = NULL;
+
+ // set the stream and draw the triangle strip
+ result = (*d3dintf->device.set_stream_source)(d3d->device, 0, d3d->vertexbuf, sizeof(d3d_vertex));
+ if (result != D3D_OK) mame_printf_verbose("Direct3D: Error %08X during device set_stream_source call\n", (int)result);
+ result = (*d3dintf->device.draw_primitive)(d3d->device, D3DPT_TRIANGLESTRIP, 0, 2);
+ if (result != D3D_OK) mame_printf_verbose("Direct3D: Error %08X during device draw_primitive call\n", (int)result);
+
+ // end the scene
+ result = (*d3dintf->device.end_scene)(d3d->device);
+ if (result != D3D_OK) mame_printf_verbose("Direct3D: Error %08X during device end_scene call\n", (int)result);
+
+ // reset the render target and release our reference to the backbuffer
+ result = (*d3dintf->device.set_render_target)(d3d->device, 0, backbuffer);
+ if (result != D3D_OK) mame_printf_verbose("Direct3D: Error %08X during device set_render_target call\n", (int)result);
+ (*d3dintf->surface.release)(backbuffer);
+ reset_render_states(d3d);
+ }
+
+ // release our reference to the target surface
+ (*d3dintf->surface.release)(surface);
+}
+
+
+
+//============================================================
+// texture_find
+//============================================================
+
+static texture_info *texture_find(d3d_info *d3d, const render_primitive *prim)
+{
+ UINT32 texhash = texture_compute_hash(&prim->texture, prim->flags);
+ texture_info *texture;
+
+ // find a match
+ for (texture = d3d->texlist; texture != NULL; texture = texture->next)
+ if (texture->hash == texhash &&
+ texture->texinfo.base == prim->texture.base &&
+ texture->texinfo.width == prim->texture.width &&
+ texture->texinfo.height == prim->texture.height &&
+ ((texture->flags ^ prim->flags) & (PRIMFLAG_BLENDMODE_MASK | PRIMFLAG_TEXFORMAT_MASK)) == 0)
+ return texture;
+
+ // nothing found
+ return NULL;
+}
+
+
+
+//============================================================
+// texture_update
+//============================================================
+
+static void texture_update(d3d_info *d3d, const render_primitive *prim)
+{
+ texture_info *texture = texture_find(d3d, prim);
+
+ // if we didn't find one, create a new texture
+ if (texture == NULL)
+ texture = texture_create(d3d, &prim->texture, prim->flags);
+
+ // if we found it, but with a different seqid, copy the data
+ if (texture->texinfo.seqid != prim->texture.seqid)
+ {
+ texture_set_data(d3d, texture, &prim->texture, prim->flags);
+ texture->texinfo.seqid = prim->texture.seqid;
+ }
+}
diff --git a/src/osd/windows/drawdd.c b/src/osd/windows/drawdd.c
new file mode 100644
index 00000000000..19bbaaee24b
--- /dev/null
+++ b/src/osd/windows/drawdd.c
@@ -0,0 +1,1435 @@
+//============================================================
+//
+// drawdd.c - Win32 DirectDraw implementation
+//
+// Copyright (c) 1996-2007, Nicola Salmoria and the MAME Team.
+// Visit http://mamedev.org for licensing and usage restrictions.
+//
+//============================================================
+
+// needed for multimonitor
+#define _WIN32_WINNT 0x501
+
+// standard windows headers
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#include <mmsystem.h>
+#include <ddraw.h>
+
+// standard C headers
+#include <math.h>
+
+// MAME headers
+#include "render.h"
+#include "rendutil.h"
+#include "options.h"
+
+// MAMEOS headers
+#include "winmain.h"
+#include "window.h"
+#include "config.h"
+
+
+
+//============================================================
+// DEBUGGING
+//============================================================
+
+
+
+//============================================================
+// CONSTANTS
+//============================================================
+
+
+
+//============================================================
+// MACROS
+//============================================================
+
+
+
+//============================================================
+// TYPE DEFINITIONS
+//============================================================
+
+typedef HRESULT (WINAPI *directdrawcreateex_ptr)(GUID FAR *lpGuid, LPVOID *lplpDD, REFIID iid, IUnknown FAR *pUnkOuter);
+typedef HRESULT (WINAPI *directdrawenumerateex_ptr)(LPDDENUMCALLBACKEXA lpCallback, LPVOID lpContext, DWORD dwFlags);
+
+
+/* dd_info is the information about DirectDraw for the current screen */
+typedef struct _dd_info dd_info;
+struct _dd_info
+{
+ GUID adapter; // current display adapter
+ GUID * adapter_ptr; // pointer to current display adapter
+ int width, height; // current width, height
+ int refresh; // current refresh rate
+ int clearouter; // clear the outer areas?
+
+ INT32 blitwidth, blitheight; // current blit width/height values
+ RECT lastdest; // last destination rectangle
+
+ IDirectDraw7 * ddraw; // pointer to the DirectDraw object
+ IDirectDrawSurface7 * primary; // pointer to the primary surface object
+ IDirectDrawSurface7 * back; // pointer to the back buffer surface object
+ IDirectDrawSurface7 * blit; // pointer to the blit surface object
+ IDirectDrawClipper * clipper; // pointer to the clipper object
+ IDirectDrawGammaControl *gamma; // pointer to the gamma control object
+
+ DDSURFACEDESC2 primarydesc; // description of the primary surface
+ DDSURFACEDESC2 blitdesc; // description of the blitting surface
+ DDSURFACEDESC2 origmode; // original video mode
+
+ DDCAPS ddcaps; // capabilities of the device
+ DDCAPS helcaps; // capabilities of the hardware
+
+ void * membuffer; // memory buffer for complex rendering
+ UINT32 membuffersize; // current size of the memory buffer
+};
+
+
+/* monitor_enum_info holds information during a monitor enumeration */
+typedef struct _monitor_enum_info monitor_enum_info;
+struct _monitor_enum_info
+{
+ win_monitor_info * monitor; // pointer to monitor we want
+ GUID guid; // GUID of the one we found
+ GUID * guid_ptr; // pointer to our GUID
+ int foundit; // TRUE if we found what we wanted
+};
+
+
+/* mode_enum_info holds information during a display mode enumeration */
+typedef struct _mode_enum_info mode_enum_info;
+struct _mode_enum_info
+{
+ win_window_info * window;
+ INT32 minimum_width, minimum_height;
+ INT32 target_width, target_height;
+ double target_refresh;
+ float best_score;
+};
+
+
+
+//============================================================
+// GLOBALS
+//============================================================
+
+static HINSTANCE dllhandle;
+static directdrawcreateex_ptr directdrawcreateex;
+static directdrawenumerateex_ptr directdrawenumerateex;
+
+
+
+//============================================================
+// INLINES
+//============================================================
+
+INLINE void update_outer_rects(dd_info *dd)
+{
+ dd->clearouter = (dd->back != NULL) ? 3 : 1;
+}
+
+
+INLINE int better_mode(int width0, int height0, int width1, int height1, float desired_aspect)
+{
+ float aspect0 = (float)width0 / (float)height0;
+ float aspect1 = (float)width1 / (float)height1;
+ return (fabs(desired_aspect - aspect0) < fabs(desired_aspect - aspect1)) ? 0 : 1;
+}
+
+
+
+//============================================================
+// PROTOTYPES
+//============================================================
+
+// core functions
+static void drawdd_exit(void);
+static int drawdd_window_init(win_window_info *window);
+static void drawdd_window_destroy(win_window_info *window);
+static const render_primitive_list *drawdd_window_get_primitives(win_window_info *window);
+static int drawdd_window_draw(win_window_info *window, HDC dc, int update);
+
+// surface management
+static int ddraw_create(win_window_info *window);
+static int ddraw_create_surfaces(win_window_info *window);
+static void ddraw_delete(win_window_info *window);
+static void ddraw_delete_surfaces(win_window_info *window);
+static int ddraw_verify_caps(dd_info *dd);
+static int ddraw_test_cooperative(win_window_info *window);
+static HRESULT create_surface(dd_info *dd, DDSURFACEDESC2 *desc, IDirectDrawSurface7 **surface, const char *type);
+static int create_clipper(win_window_info *window);
+
+// drawing helpers
+static void compute_blit_surface_size(win_window_info *window);
+static void blit_to_primary(win_window_info *window, int srcwidth, int srcheight);
+
+// video modes
+static int config_adapter_mode(win_window_info *window);
+static void get_adapter_for_monitor(dd_info *dd, win_monitor_info *monitor);
+static void pick_best_mode(win_window_info *window);
+
+// rendering
+static void drawdd_rgb888_draw_primitives(const render_primitive *primlist, void *dstdata, UINT32 width, UINT32 height, UINT32 pitch);
+static void drawdd_bgr888_draw_primitives(const render_primitive *primlist, void *dstdata, UINT32 width, UINT32 height, UINT32 pitch);
+static void drawdd_rgb565_draw_primitives(const render_primitive *primlist, void *dstdata, UINT32 width, UINT32 height, UINT32 pitch);
+static void drawdd_rgb555_draw_primitives(const render_primitive *primlist, void *dstdata, UINT32 width, UINT32 height, UINT32 pitch);
+static void drawdd_rgb888_nr_draw_primitives(const render_primitive *primlist, void *dstdata, UINT32 width, UINT32 height, UINT32 pitch);
+static void drawdd_bgr888_nr_draw_primitives(const render_primitive *primlist, void *dstdata, UINT32 width, UINT32 height, UINT32 pitch);
+static void drawdd_rgb565_nr_draw_primitives(const render_primitive *primlist, void *dstdata, UINT32 width, UINT32 height, UINT32 pitch);
+static void drawdd_rgb555_nr_draw_primitives(const render_primitive *primlist, void *dstdata, UINT32 width, UINT32 height, UINT32 pitch);
+
+
+
+//============================================================
+// drawdd_init
+//============================================================
+
+int drawdd_init(win_draw_callbacks *callbacks)
+{
+ // dynamically grab the create function from ddraw.dll
+ dllhandle = LoadLibrary(TEXT("ddraw.dll"));
+ if (dllhandle == NULL)
+ {
+ mame_printf_verbose("DirectDraw: Unable to access ddraw.dll\n");
+ return 1;
+ }
+
+ // import the create function
+ directdrawcreateex = (directdrawcreateex_ptr)GetProcAddress(dllhandle, "DirectDrawCreateEx");
+ if (directdrawcreateex == NULL)
+ {
+ mame_printf_verbose("DirectDraw: Unable to find DirectDrawCreateEx\n");
+ FreeLibrary(dllhandle);
+ dllhandle = NULL;
+ return 1;
+ }
+
+ // import the enumerate function
+ directdrawenumerateex = (directdrawenumerateex_ptr)GetProcAddress(dllhandle, "DirectDrawEnumerateExA");
+ if (directdrawenumerateex == NULL)
+ {
+ mame_printf_verbose("DirectDraw: Unable to find DirectDrawEnumerateExA\n");
+ FreeLibrary(dllhandle);
+ dllhandle = NULL;
+ return 1;
+ }
+
+ // fill in the callbacks
+ callbacks->exit = drawdd_exit;
+ callbacks->window_init = drawdd_window_init;
+ callbacks->window_get_primitives = drawdd_window_get_primitives;
+ callbacks->window_draw = drawdd_window_draw;
+ callbacks->window_destroy = drawdd_window_destroy;
+
+ mame_printf_verbose("DirectDraw: Using DirectDraw 7\n");
+ return 0;
+}
+
+
+
+//============================================================
+// drawdd_exit
+//============================================================
+
+static void drawdd_exit(void)
+{
+ if (dllhandle != NULL)
+ FreeLibrary(dllhandle);
+}
+
+
+
+//============================================================
+// drawdd_window_init
+//============================================================
+
+static int drawdd_window_init(win_window_info *window)
+{
+ dd_info *dd;
+
+ // allocate memory for our structures
+ dd = malloc_or_die(sizeof(*dd));
+ memset(dd, 0, sizeof(*dd));
+ window->drawdata = dd;
+
+ // configure the adapter for the mode we want
+ if (config_adapter_mode(window))
+ goto error;
+
+ // create the ddraw object
+ if (ddraw_create(window))
+ goto error;
+
+ return 0;
+
+error:
+ drawdd_window_destroy(window);
+ mame_printf_error("Unable to initialize DirectDraw.\n");
+ return 1;
+}
+
+
+
+//============================================================
+// drawdd_window_destroy
+//============================================================
+
+static void drawdd_window_destroy(win_window_info *window)
+{
+ dd_info *dd = window->drawdata;
+
+ // skip if nothing
+ if (dd == NULL)
+ return;
+
+ // delete the ddraw object
+ ddraw_delete(window);
+
+ // free the memory in the window
+ free(dd);
+ window->drawdata = NULL;
+}
+
+
+
+//============================================================
+// drawdd_window_get_primitives
+//============================================================
+
+static const render_primitive_list *drawdd_window_get_primitives(win_window_info *window)
+{
+ dd_info *dd = window->drawdata;
+
+ compute_blit_surface_size(window);
+ render_target_set_bounds(window->target, dd->blitwidth, dd->blitheight, 0);
+ render_target_set_max_update_rate(window->target, (dd->refresh == 0) ? dd->origmode.dwRefreshRate : dd->refresh);
+
+ return render_target_get_primitives(window->target);
+}
+
+
+
+//============================================================
+// drawdd_window_draw
+//============================================================
+
+static int drawdd_window_draw(win_window_info *window, HDC dc, int update)
+{
+ dd_info *dd = window->drawdata;
+ const render_primitive *prim;
+ int usemembuffer = FALSE;
+ HRESULT result;
+
+ // if we haven't been created, just punt
+ if (dd == NULL)
+ return 1;
+
+ // if we're updating, remember to erase the outer stuff
+ if (update)
+ update_outer_rects(dd);
+
+ // if we have a ddraw object, check the cooperative level
+ if (ddraw_test_cooperative(window))
+ return 1;
+
+ // get the size; if we're too small, delete the existing surfaces
+ if (dd->blitwidth > dd->blitdesc.dwWidth || dd->blitheight > dd->blitdesc.dwHeight)
+ ddraw_delete_surfaces(window);
+
+ // if we need to create surfaces, do it now
+ if (dd->blit == NULL && ddraw_create_surfaces(window) != 0)
+ return 1;
+
+ // select our surface and lock it
+ result = IDirectDrawSurface7_Lock(dd->blit, NULL, &dd->blitdesc, DDLOCK_WAIT, NULL);
+ if (result == DDERR_SURFACELOST)
+ {
+ mame_printf_verbose("DirectDraw: Lost surfaces; deleting and retrying next frame\n");
+ ddraw_delete_surfaces(window);
+ return 1;
+ }
+ if (result != DD_OK)
+ {
+ mame_printf_verbose("DirectDraw: Error %08X locking blit surface\n", (int)result);
+ return 1;
+ }
+
+ // render to it
+ osd_lock_acquire(window->primlist->lock);
+
+ // scan the list of primitives for tricky stuff
+ for (prim = window->primlist->head; prim != NULL; prim = prim->next)
+ if (PRIMFLAG_GET_BLENDMODE(prim->flags) != BLENDMODE_NONE ||
+ (prim->texture.base != NULL && PRIMFLAG_GET_TEXFORMAT(prim->flags) == TEXFORMAT_ARGB32))
+ {
+ usemembuffer = TRUE;
+ break;
+ }
+
+ // if we're using the memory buffer, draw offscreen first and then copy
+ if (usemembuffer)
+ {
+ int x, y;
+
+ // based on the target format, use one of our standard renderers
+ switch (dd->blitdesc.ddpfPixelFormat.dwRBitMask)
+ {
+ case 0x00ff0000: drawdd_rgb888_draw_primitives(window->primlist->head, dd->membuffer, dd->blitwidth, dd->blitheight, dd->blitwidth); break;
+ case 0x000000ff: drawdd_bgr888_draw_primitives(window->primlist->head, dd->membuffer, dd->blitwidth, dd->blitheight, dd->blitwidth); break;
+ case 0xf800: drawdd_rgb565_draw_primitives(window->primlist->head, dd->membuffer, dd->blitwidth, dd->blitheight, dd->blitwidth); break;
+ case 0x7c00: drawdd_rgb555_draw_primitives(window->primlist->head, dd->membuffer, dd->blitwidth, dd->blitheight, dd->blitwidth); break;
+ default:
+ mame_printf_verbose("DirectDraw: Unknown target mode: R=%08X G=%08X B=%08X\n", (int)dd->blitdesc.ddpfPixelFormat.dwRBitMask, (int)dd->blitdesc.ddpfPixelFormat.dwGBitMask, (int)dd->blitdesc.ddpfPixelFormat.dwBBitMask);
+ break;
+ }
+
+ // handle copying to both 16bpp and 32bpp destinations
+ for (y = 0; y < dd->blitheight; y++)
+ {
+ if (dd->blitdesc.ddpfPixelFormat.dwRGBBitCount == 32)
+ {
+ UINT32 *src = (UINT32 *)dd->membuffer + y * dd->blitwidth;
+ UINT32 *dst = (UINT32 *)((UINT8 *)dd->blitdesc.lpSurface + y * dd->blitdesc.lPitch);
+ for (x = 0; x < dd->blitwidth; x++)
+ *dst++ = *src++;
+ }
+ else if (dd->blitdesc.ddpfPixelFormat.dwRGBBitCount == 16)
+ {
+ UINT16 *src = (UINT16 *)dd->membuffer + y * dd->blitwidth;
+ UINT16 *dst = (UINT16 *)((UINT8 *)dd->blitdesc.lpSurface + y * dd->blitdesc.lPitch);
+ for (x = 0; x < dd->blitwidth; x++)
+ *dst++ = *src++;
+ }
+ }
+
+ }
+
+ // otherwise, draw directly
+ else
+ {
+ // based on the target format, use one of our standard renderers
+ switch (dd->blitdesc.ddpfPixelFormat.dwRBitMask)
+ {
+ case 0x00ff0000: drawdd_rgb888_nr_draw_primitives(window->primlist->head, dd->blitdesc.lpSurface, dd->blitwidth, dd->blitheight, dd->blitdesc.lPitch / 4); break;
+ case 0x000000ff: drawdd_bgr888_nr_draw_primitives(window->primlist->head, dd->blitdesc.lpSurface, dd->blitwidth, dd->blitheight, dd->blitdesc.lPitch / 4); break;
+ case 0xf800: drawdd_rgb565_nr_draw_primitives(window->primlist->head, dd->blitdesc.lpSurface, dd->blitwidth, dd->blitheight, dd->blitdesc.lPitch / 2); break;
+ case 0x7c00: drawdd_rgb555_nr_draw_primitives(window->primlist->head, dd->blitdesc.lpSurface, dd->blitwidth, dd->blitheight, dd->blitdesc.lPitch / 2); break;
+ default:
+ mame_printf_verbose("DirectDraw: Unknown target mode: R=%08X G=%08X B=%08X\n", (int)dd->blitdesc.ddpfPixelFormat.dwRBitMask, (int)dd->blitdesc.ddpfPixelFormat.dwGBitMask, (int)dd->blitdesc.ddpfPixelFormat.dwBBitMask);
+ break;
+ }
+ }
+ osd_lock_release(window->primlist->lock);
+
+ // unlock and blit
+ result = IDirectDrawSurface7_Unlock(dd->blit, NULL);
+ if (result != DD_OK) mame_printf_verbose("DirectDraw: Error %08X unlocking blit surface\n", (int)result);
+
+ // sync to VBLANK
+ if ((video_config.waitvsync || video_config.syncrefresh) && video_get_throttle() && (!window->fullscreen || dd->back == NULL))
+ {
+ result = IDirectDraw7_WaitForVerticalBlank(dd->ddraw, DDWAITVB_BLOCKBEGIN, NULL);
+ if (result != DD_OK) mame_printf_verbose("DirectDraw: Error %08X waiting for VBLANK\n", (int)result);
+ }
+
+ // complete the blitting
+ blit_to_primary(window, dd->blitwidth, dd->blitheight);
+ return 0;
+}
+
+
+
+//============================================================
+// ddraw_create
+//============================================================
+
+static int ddraw_create(win_window_info *window)
+{
+ dd_info *dd = window->drawdata;
+ HRESULT result;
+ int verify;
+
+ // if a device exists, free it
+ if (dd->ddraw != NULL)
+ ddraw_delete(window);
+
+ // create the DirectDraw object
+ result = (*directdrawcreateex)(dd->adapter_ptr, (LPVOID *)&dd->ddraw, &IID_IDirectDraw7, NULL);
+ if (result != DD_OK)
+ {
+ mame_printf_verbose("DirectDraw: Error %08X during DirectDrawCreateEx call\n", (int)result);
+ goto error;
+ }
+
+ // verify the caps
+ verify = ddraw_verify_caps(dd);
+ if (verify == 2)
+ {
+ mame_printf_error("DirectDraw: Error - Device does not meet minimum requirements for DirectDraw rendering\n");
+ goto error;
+ }
+ if (verify == 1)
+ mame_printf_verbose("DirectDraw: Warning - Device may not perform well for DirectDraw rendering\n");
+
+ // set the cooperative level
+ // for non-window modes, we will use full screen here
+ result = IDirectDraw7_SetCooperativeLevel(dd->ddraw, win_window_list->hwnd, DDSCL_SETFOCUSWINDOW);
+ if (result != DD_OK)
+ {
+ mame_printf_verbose("DirectDraw: Error %08X during IDirectDraw7_SetCooperativeLevel(FOCUSWINDOW) call\n", (int)result);
+ goto error;
+ }
+ result = IDirectDraw7_SetCooperativeLevel(dd->ddraw, window->hwnd, DDSCL_SETDEVICEWINDOW | (window->fullscreen ? DDSCL_FULLSCREEN | DDSCL_EXCLUSIVE : DDSCL_NORMAL));
+ if (result != DD_OK)
+ {
+ mame_printf_verbose("DirectDraw: Error %08X during IDirectDraw7_SetCooperativeLevel(DEVICEWINDOW) call\n", (int)result);
+ goto error;
+ }
+
+ // full screen mode: set the resolution
+ if (window->fullscreen && video_config.switchres)
+ {
+ result = IDirectDraw7_SetDisplayMode(dd->ddraw, dd->width, dd->height, 32, dd->refresh, 0);
+ if (result != DD_OK)
+ {
+ mame_printf_verbose("DirectDraw: Error %08X attempting to set video mode %dx%d@%d call\n", (int)result, dd->width, dd->height, dd->refresh);
+ goto error;
+ }
+ }
+
+ return ddraw_create_surfaces(window);
+
+error:
+ ddraw_delete(window);
+ return 1;
+}
+
+
+
+//============================================================
+// ddraw_create_surfaces
+//============================================================
+
+static int ddraw_create_surfaces(win_window_info *window)
+{
+ dd_info *dd = window->drawdata;
+ HRESULT result;
+
+ // make a description of the primary surface
+ memset(&dd->primarydesc, 0, sizeof(dd->primarydesc));
+ dd->primarydesc.dwSize = sizeof(dd->primarydesc);
+ dd->primarydesc.dwFlags = DDSD_CAPS;
+ dd->primarydesc.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE;
+
+ // for triple-buffered full screen mode, allocate flipping surfaces
+ if (window->fullscreen && video_config.triplebuf)
+ {
+ dd->primarydesc.dwFlags |= DDSD_BACKBUFFERCOUNT;
+ dd->primarydesc.ddsCaps.dwCaps |= DDSCAPS_FLIP | DDSCAPS_COMPLEX;
+ dd->primarydesc.dwBackBufferCount = 2;
+ }
+
+ // create the primary surface and report errors
+ result = create_surface(dd, &dd->primarydesc, &dd->primary, "primary");
+ if (result != DD_OK) goto error;
+
+ // full screen mode: get the back surface
+ dd->back = NULL;
+ if (window->fullscreen && video_config.triplebuf)
+ {
+ DDSCAPS2 caps = { DDSCAPS_BACKBUFFER };
+ result = IDirectDrawSurface7_GetAttachedSurface(dd->primary, &caps, &dd->back);
+ if (result != DD_OK)
+ {
+ mame_printf_verbose("DirectDraw: Error %08X getting attached back surface\n", (int)result);
+ goto error;
+ }
+ }
+
+ // now make a description of our blit surface, based on the primary surface
+ if (dd->blitwidth == 0 || dd->blitheight == 0)
+ compute_blit_surface_size(window);
+ dd->blitdesc = dd->primarydesc;
+ dd->blitdesc.dwFlags = DDSD_WIDTH | DDSD_HEIGHT | DDSD_PIXELFORMAT | DDSD_CAPS;
+ dd->blitdesc.dwWidth = dd->blitwidth;
+ dd->blitdesc.dwHeight = dd->blitheight;
+ dd->blitdesc.ddsCaps.dwCaps = DDSCAPS_VIDEOMEMORY;
+
+ // then create the blit surface, fall back to system memory if video mem doesn't work
+ result = create_surface(dd, &dd->blitdesc, &dd->blit, "blit");
+ if (result != DD_OK)
+ {
+ dd->blitdesc.ddsCaps.dwCaps = DDSCAPS_SYSTEMMEMORY;
+ result = create_surface(dd, &dd->blitdesc, &dd->blit, "blit");
+ }
+ if (result != DD_OK) goto error;
+
+ // create a memory buffer for offscreen drawing
+ if (dd->membuffersize < dd->blitwidth * dd->blitheight * 4)
+ {
+ dd->membuffersize = dd->blitwidth * dd->blitheight * 4;
+ dd->membuffer = realloc(dd->membuffer, dd->membuffersize);
+ }
+ if (dd->membuffer == NULL)
+ goto error;
+
+#ifdef MESS
+ // create a clipper for all modes, since MESS has dialogs
+ if (create_clipper(window))
+ goto error;
+#else
+ // create a clipper for windowed mode
+ if (!window->fullscreen && create_clipper(window))
+ goto error;
+#endif
+
+ // full screen mode: set the gamma
+ if (window->fullscreen)
+ {
+ // only set the gamma if it's not 1.0f
+ float brightness = options_get_float(mame_options(), WINOPTION_FULLSCREENBRIGHTNESS);
+ float contrast = options_get_float(mame_options(), WINOPTION_FULLLSCREENCONTRAST);
+ float gamma = options_get_float(mame_options(), WINOPTION_FULLSCREENGAMMA);
+ if (brightness != 1.0f || contrast != 1.0f || gamma != 1.0f)
+ {
+ // see if we can get a GammaControl object
+ result = IDirectDrawSurface_QueryInterface(dd->primary, &IID_IDirectDrawGammaControl, (void **)&dd->gamma);
+ if (result != DD_OK)
+ {
+ mame_printf_warning("DirectDraw: Warning - device does not support full screen gamma correction.\n");
+ dd->gamma = NULL;
+ }
+
+ // proceed if we can
+ if (dd->gamma != NULL)
+ {
+ DDGAMMARAMP ramp;
+ int i;
+
+ // create a standard ramp and set it
+ for (i = 0; i < 256; i++)
+ ramp.red[i] = ramp.green[i] = ramp.blue[i] = apply_brightness_contrast_gamma(i, brightness, contrast, gamma) << 8;
+
+ // attempt to set it
+ result = IDirectDrawGammaControl_SetGammaRamp(dd->gamma, 0, &ramp);
+ if (result != DD_OK)
+ mame_printf_verbose("DirectDraw: Error %08X attempting to set gamma correction.\n", (int)result);
+ }
+ }
+ }
+
+ // force some updates
+ update_outer_rects(dd);
+ return 0;
+
+error:
+ ddraw_delete_surfaces(window);
+ return 1;
+}
+
+
+
+//============================================================
+// ddraw_delete
+//============================================================
+
+static void ddraw_delete(win_window_info *window)
+{
+ dd_info *dd = window->drawdata;
+
+ // free surfaces
+ ddraw_delete_surfaces(window);
+
+ // restore resolutions
+ if (dd->ddraw != NULL)
+ IDirectDraw7_RestoreDisplayMode(dd->ddraw);
+
+ // reset cooperative level
+ if (dd->ddraw != NULL && window->hwnd != NULL)
+ IDirectDraw7_SetCooperativeLevel(dd->ddraw, window->hwnd, DDSCL_NORMAL);
+
+ // release the DirectDraw object itself
+ if (dd->ddraw != NULL)
+ IDirectDraw7_Release(dd->ddraw);
+ dd->ddraw = NULL;
+}
+
+
+
+//============================================================
+// ddraw_delete_surfaces
+//============================================================
+
+static void ddraw_delete_surfaces(win_window_info *window)
+{
+ dd_info *dd = window->drawdata;
+
+ // release the gamma control
+ if (dd->gamma != NULL)
+ IDirectDrawGammaControl_Release(dd->gamma);
+ dd->gamma = NULL;
+
+ // release the clipper
+ if (dd->clipper != NULL)
+ IDirectDrawClipper_Release(dd->clipper);
+ dd->clipper = NULL;
+
+ // free the memory buffer
+ if (dd->membuffer != NULL)
+ free(dd->membuffer);
+ dd->membuffer = NULL;
+ dd->membuffersize = 0;
+
+ // release the blit surface
+ if (dd->blit != NULL)
+ IDirectDrawSurface7_Release(dd->blit);
+ dd->blit = NULL;
+
+ // release the back surface
+ if (dd->back != NULL)
+ IDirectDrawSurface7_Release(dd->back);
+ dd->back = NULL;
+
+ // release the primary surface
+ if (dd->primary != NULL)
+ IDirectDrawSurface7_Release(dd->primary);
+ dd->primary = NULL;
+}
+
+
+
+//============================================================
+// ddraw_verify_caps
+//============================================================
+
+static int ddraw_verify_caps(dd_info *dd)
+{
+ int retval = 0;
+ HRESULT result;
+
+ // get the capabilities
+ dd->ddcaps.dwSize = sizeof(dd->ddcaps);
+ dd->helcaps.dwSize = sizeof(dd->helcaps);
+ result = IDirectDraw7_GetCaps(dd->ddraw, &dd->ddcaps, &dd->helcaps);
+ if (result != DD_OK)
+ {
+ mame_printf_verbose("DirectDraw: Error %08X during IDirectDraw7_GetCaps call\n", (int)result);
+ return 1;
+ }
+
+ // determine if hardware stretching is available
+ if ((dd->ddcaps.dwCaps & DDCAPS_BLTSTRETCH) == 0)
+ {
+ mame_printf_verbose("DirectDraw: Warning - Device does not support hardware stretching\n");
+ retval = 1;
+ }
+
+ return retval;
+}
+
+
+
+//============================================================
+// ddraw_test_cooperative
+//============================================================
+
+static int ddraw_test_cooperative(win_window_info *window)
+{
+ dd_info *dd = window->drawdata;
+ HRESULT result;
+
+ // check our current status; if we lost the device, punt to GDI
+ result = IDirectDraw7_TestCooperativeLevel(dd->ddraw);
+ switch (result)
+ {
+ // punt to GDI if someone else has exclusive mode
+ case DDERR_NOEXCLUSIVEMODE:
+ case DDERR_EXCLUSIVEMODEALREADYSET:
+ ddraw_delete_surfaces(window);
+ return 1;
+
+ // if we're ok, but we don't have a primary surface, create one
+ default:
+ case DD_OK:
+ if (dd->primary == NULL)
+ return ddraw_create_surfaces(window);
+ return 0;
+ }
+}
+
+
+
+//============================================================
+// create_surface
+//============================================================
+
+static HRESULT create_surface(dd_info *dd, DDSURFACEDESC2 *desc, IDirectDrawSurface7 **surface, const char *type)
+{
+ HRESULT result;
+
+ // create the surface as requested
+ result = IDirectDraw7_CreateSurface(dd->ddraw, desc, surface, NULL);
+ if (result != DD_OK)
+ {
+ mame_printf_verbose("DirectDraw: Error %08X creating %s surface\n", (int)result, type);
+ return result;
+ }
+
+ // get a description of the primary surface
+ result = IDirectDrawSurface7_GetSurfaceDesc(*surface, desc);
+ if (result != DD_OK)
+ {
+ mame_printf_verbose("DirectDraw: Error %08X getting %s surface desciption\n", (int)result, type);
+ IDirectDrawSurface7_Release(*surface);
+ *surface = NULL;
+ return result;
+ }
+
+ // print out the good stuff
+ mame_printf_verbose("DirectDraw: %s surface created: %dx%dx%d (R=%08X G=%08X B=%08X)\n",
+ type,
+ (int)desc->dwWidth,
+ (int)desc->dwHeight,
+ (int)desc->ddpfPixelFormat.dwRGBBitCount,
+ (UINT32)desc->ddpfPixelFormat.dwRBitMask,
+ (UINT32)desc->ddpfPixelFormat.dwGBitMask,
+ (UINT32)desc->ddpfPixelFormat.dwBBitMask);
+ return result;
+}
+
+
+
+//============================================================
+// create_clipper
+//============================================================
+
+static int create_clipper(win_window_info *window)
+{
+ dd_info *dd = window->drawdata;
+ HRESULT result;
+
+ // create a clipper for the primary surface
+ result = IDirectDraw7_CreateClipper(dd->ddraw, 0, &dd->clipper, NULL);
+ if (result != DD_OK)
+ {
+ mame_printf_verbose("DirectDraw: Error %08X creating clipper\n", (int)result);
+ return 1;
+ }
+
+ // set the clipper's hwnd
+ result = IDirectDrawClipper_SetHWnd(dd->clipper, 0, window->hwnd);
+ if (result != DD_OK)
+ {
+ mame_printf_verbose("DirectDraw: Error %08X setting clipper hwnd\n", (int)result);
+ return 1;
+ }
+
+ // set the clipper on the primary surface
+ result = IDirectDrawSurface7_SetClipper(dd->primary, dd->clipper);
+ if (result != DD_OK)
+ {
+ mame_printf_verbose("DirectDraw: Error %08X setting clipper on primary surface\n", (int)result);
+ return 1;
+ }
+ return 0;
+}
+
+
+
+//============================================================
+// compute_blit_surface_size
+//============================================================
+
+static void compute_blit_surface_size(win_window_info *window)
+{
+ dd_info *dd = window->drawdata;
+ INT32 newwidth, newheight;
+ int xscale, yscale;
+ RECT client;
+
+ // start with the minimum size
+ render_target_get_minimum_size(window->target, &newwidth, &newheight);
+
+ // get the window's client rectangle
+ GetClientRect(window->hwnd, &client);
+
+ // hardware stretch case: apply prescale
+ if (video_config.hwstretch)
+ {
+ int prescale = (video_config.prescale < 1) ? 1 : video_config.prescale;
+
+ // clamp the prescale to something smaller than the target bounds
+ xscale = prescale;
+ while (xscale > 1 && newwidth * xscale > rect_width(&client))
+ xscale--;
+ yscale = prescale;
+ while (yscale > 1 && newheight * yscale > rect_height(&client))
+ yscale--;
+ }
+
+ // non stretch case
+ else
+ {
+ INT32 target_width = rect_width(&client);
+ INT32 target_height = rect_height(&client);
+ float desired_aspect = 1.0f;
+
+ // compute the appropriate visible area if we're trying to keepaspect
+ if (video_config.keepaspect)
+ {
+ win_monitor_info *monitor = winwindow_video_window_monitor(window, NULL);
+ render_target_compute_visible_area(window->target, target_width, target_height, winvideo_monitor_get_aspect(monitor), render_target_get_orientation(window->target), &target_width, &target_height);
+ desired_aspect = (float)target_width / (float)target_height;
+ }
+
+ // compute maximum integral scaling to fit the window
+ xscale = (target_width + 2) / newwidth;
+ yscale = (target_height + 2) / newheight;
+
+ // try a little harder to keep the aspect ratio if desired
+ if (video_config.keepaspect)
+ {
+ // if we could stretch more in the X direction, and that makes a better fit, bump the xscale
+ while (newwidth * (xscale + 1) <= rect_width(&client) &&
+ better_mode(newwidth * xscale, newheight * yscale, newwidth * (xscale + 1), newheight * yscale, desired_aspect))
+ xscale++;
+
+ // if we could stretch more in the Y direction, and that makes a better fit, bump the yscale
+ while (newheight * (yscale + 1) <= rect_height(&client) &&
+ better_mode(newwidth * xscale, newheight * yscale, newwidth * xscale, newheight * (yscale + 1), desired_aspect))
+ yscale++;
+
+ // now that we've maxed out, see if backing off the maximally stretched one makes a better fit
+ if (rect_width(&client) - newwidth * xscale < rect_height(&client) - newheight * yscale)
+ {
+ while (xscale > 1 && better_mode(newwidth * xscale, newheight * yscale, newwidth * (xscale - 1), newheight * yscale, desired_aspect))
+ xscale--;
+ }
+ else
+ {
+ while (yscale > 1 && better_mode(newwidth * xscale, newheight * yscale, newwidth * xscale, newheight * (yscale - 1), desired_aspect))
+ yscale--;
+ }
+ }
+ }
+
+ // ensure at least a scale factor of 1
+ if (xscale == 0) xscale = 1;
+ if (yscale == 0) yscale = 1;
+
+ // apply the final scale
+ newwidth *= xscale;
+ newheight *= yscale;
+ if (newwidth != dd->blitwidth || newheight != dd->blitheight)
+ {
+ // force some updates
+ update_outer_rects(dd);
+ mame_printf_verbose("DirectDraw: New blit size = %dx%d\n", newwidth, newheight);
+ }
+ dd->blitwidth = newwidth;
+ dd->blitheight = newheight;
+}
+
+
+
+//============================================================
+// calc_fullscreen_margins
+//============================================================
+
+static void calc_fullscreen_margins(win_window_info *window, DWORD desc_width, DWORD desc_height, RECT *margins)
+{
+ margins->left = 0;
+ margins->top = 0;
+ margins->right = desc_width;
+ margins->bottom = desc_height;
+
+ if (win_has_menu(window))
+ {
+ static int height_with_menubar = 0;
+ if (height_with_menubar == 0)
+ {
+ RECT with_menu = { 100, 100, 200, 200 };
+ RECT without_menu = { 100, 100, 200, 200 };
+ AdjustWindowRect(&with_menu, WS_OVERLAPPED, TRUE);
+ AdjustWindowRect(&without_menu, WS_OVERLAPPED, FALSE);
+ height_with_menubar = (with_menu.bottom - with_menu.top) - (without_menu.bottom - without_menu.top);
+ }
+ margins->top = height_with_menubar;
+ }
+}
+
+
+
+//============================================================
+// blit_to_primary
+//============================================================
+
+static void blit_to_primary(win_window_info *window, int srcwidth, int srcheight)
+{
+ dd_info *dd = window->drawdata;
+ IDirectDrawSurface7 *target = (dd->back != NULL) ? dd->back : dd->primary;
+ win_monitor_info *monitor = winwindow_video_window_monitor(window, NULL);
+ DDBLTFX blitfx = { sizeof(DDBLTFX) };
+ RECT clear, outer, dest, source;
+ INT32 dstwidth, dstheight;
+ HRESULT result;
+
+ // compute source rect
+ source.left = source.top = 0;
+ source.right = srcwidth;
+ source.bottom = srcheight;
+
+ // compute outer rect -- windowed version
+ if (!window->fullscreen)
+ {
+ GetClientRect(window->hwnd, &outer);
+ ClientToScreen(window->hwnd, &((LPPOINT)&outer)[0]);
+ ClientToScreen(window->hwnd, &((LPPOINT)&outer)[1]);
+
+ // adjust to be relative to the monitor
+ outer.left -= monitor->info.rcMonitor.left;
+ outer.right -= monitor->info.rcMonitor.left;
+ outer.top -= monitor->info.rcMonitor.top;
+ outer.bottom -= monitor->info.rcMonitor.top;
+ }
+
+ // compute outer rect -- full screen version
+ else
+ {
+ calc_fullscreen_margins(window, dd->primarydesc.dwWidth, dd->primarydesc.dwHeight, &outer);
+ }
+
+ // if we're respecting the aspect ratio, we need to adjust to fit
+ dstwidth = rect_width(&outer);
+ dstheight = rect_height(&outer);
+ if (!video_config.hwstretch)
+ {
+ // trim the source if necessary
+ if (rect_width(&outer) < srcwidth)
+ {
+ source.left += (srcwidth - rect_width(&outer)) / 2;
+ source.right = source.left + rect_width(&outer);
+ }
+ if (rect_height(&outer) < srcheight)
+ {
+ source.top += (srcheight - rect_height(&outer)) / 2;
+ source.bottom = source.top + rect_height(&outer);
+ }
+
+ // match the destination and source sizes
+ dstwidth = srcwidth = source.right - source.left;
+ dstheight = srcheight = source.bottom - source.top;
+ }
+ else if (video_config.keepaspect)
+ {
+ // compute the appropriate visible area
+ render_target_compute_visible_area(window->target, rect_width(&outer), rect_height(&outer), winvideo_monitor_get_aspect(monitor), render_target_get_orientation(window->target), &dstwidth, &dstheight);
+ }
+
+ // center within
+ dest.left = outer.left + (rect_width(&outer) - dstwidth) / 2;
+ dest.right = dest.left + dstwidth;
+ dest.top = outer.top + (rect_height(&outer) - dstheight) / 2;
+ dest.bottom = dest.top + dstheight;
+
+ // compare against last destination; if different, force a redraw
+ if (dest.left != dd->lastdest.left || dest.right != dd->lastdest.right || dest.top != dd->lastdest.top || dest.bottom != dd->lastdest.bottom)
+ {
+ dd->lastdest = dest;
+ update_outer_rects(dd);
+ }
+
+ // clear outer rects if we need to
+ if (dd->clearouter != 0)
+ {
+ dd->clearouter--;
+
+ // clear the left edge
+ if (dest.left > outer.left)
+ {
+ clear = outer;
+ clear.right = dest.left;
+ result = IDirectDrawSurface_Blt(target, &clear, NULL, NULL, DDBLT_COLORFILL | DDBLT_WAIT, &blitfx);
+ if (result != DD_OK) mame_printf_verbose("DirectDraw: Error %08X clearing the screen\n", (int)result);
+ }
+
+ // clear the right edge
+ if (dest.right < outer.right)
+ {
+ clear = outer;
+ clear.left = dest.right;
+ result = IDirectDrawSurface_Blt(target, &clear, NULL, NULL, DDBLT_COLORFILL | DDBLT_WAIT, &blitfx);
+ if (result != DD_OK) mame_printf_verbose("DirectDraw: Error %08X clearing the screen\n", (int)result);
+ }
+
+ // clear the top edge
+ if (dest.top > outer.top)
+ {
+ clear = outer;
+ clear.bottom = dest.top;
+ result = IDirectDrawSurface_Blt(target, &clear, NULL, NULL, DDBLT_COLORFILL | DDBLT_WAIT, &blitfx);
+ if (result != DD_OK) mame_printf_verbose("DirectDraw: Error %08X clearing the screen\n", (int)result);
+ }
+
+ // clear the bottom edge
+ if (dest.bottom < outer.bottom)
+ {
+ clear = outer;
+ clear.top = dest.bottom;
+ result = IDirectDrawSurface_Blt(target, &clear, NULL, NULL, DDBLT_COLORFILL | DDBLT_WAIT, &blitfx);
+ if (result != DD_OK) mame_printf_verbose("DirectDraw: Error %08X clearing the screen\n", (int)result);
+ }
+ }
+
+ // do the blit
+ result = IDirectDrawSurface7_Blt(target, &dest, dd->blit, &source, DDBLT_WAIT, NULL);
+ if (result != DD_OK) mame_printf_verbose("DirectDraw: Error %08X blitting to the screen\n", (int)result);
+
+ // page flip if triple buffered
+ if (window->fullscreen && dd->back != NULL)
+ {
+ result = IDirectDrawSurface7_Flip(dd->primary, NULL, DDFLIP_WAIT);
+ if (result != DD_OK) mame_printf_verbose("DirectDraw: Error %08X waiting for VBLANK\n", (int)result);
+ }
+}
+
+
+
+//============================================================
+// config_adapter_mode
+//============================================================
+
+static int config_adapter_mode(win_window_info *window)
+{
+ DDDEVICEIDENTIFIER2 identifier;
+ dd_info *dd = window->drawdata;
+ HRESULT result;
+
+ // choose the monitor number
+ get_adapter_for_monitor(dd, window->monitor);
+
+ // create a temporary DirectDraw object
+ result = (*directdrawcreateex)(dd->adapter_ptr, (LPVOID *)&dd->ddraw, &IID_IDirectDraw7, NULL);
+ if (result != DD_OK)
+ {
+ mame_printf_verbose("DirectDraw: Error %08X during DirectDrawCreateEx call\n", (int)result);
+ return 1;
+ }
+
+ // get the identifier
+ result = IDirectDraw7_GetDeviceIdentifier(dd->ddraw, &identifier, 0);
+ if (result != DD_OK)
+ {
+ mame_printf_error("Error getting identifier for device\n");
+ return 1;
+ }
+ mame_printf_verbose("DirectDraw: Configuring device %s\n", identifier.szDescription);
+
+ // get the current display mode
+ memset(&dd->origmode, 0, sizeof(dd->origmode));
+ dd->origmode.dwSize = sizeof(dd->origmode);
+ result = IDirectDraw7_GetDisplayMode(dd->ddraw, &dd->origmode);
+ if (result != DD_OK)
+ {
+ mame_printf_verbose("DirectDraw: Error %08X getting current display mode\n", (int)result);
+ IDirectDraw7_Release(dd->ddraw);
+ return 1;
+ }
+
+ // choose a resolution: full screen mode case
+ if (window->fullscreen)
+ {
+ // default to the current mode exactly
+ dd->width = dd->origmode.dwWidth;
+ dd->height = dd->origmode.dwHeight;
+ dd->refresh = dd->origmode.dwRefreshRate;
+
+ // if we're allowed to switch resolutions, override with something better
+ if (video_config.switchres)
+ pick_best_mode(window);
+ }
+
+ // release the DirectDraw object
+ IDirectDraw7_Release(dd->ddraw);
+ dd->ddraw = NULL;
+
+ // if we're not changing resolutions, make sure we have a resolution we can handle
+ if (!window->fullscreen || !video_config.switchres)
+ {
+ switch (dd->origmode.ddpfPixelFormat.dwRBitMask)
+ {
+ case 0x00ff0000:
+ case 0x000000ff:
+ case 0xf800:
+ case 0x7c00:
+ break;
+
+ default:
+ mame_printf_verbose("DirectDraw: Unknown target mode: R=%08X G=%08X B=%08X\n", (int)dd->origmode.ddpfPixelFormat.dwRBitMask, (int)dd->origmode.ddpfPixelFormat.dwGBitMask, (int)dd->origmode.ddpfPixelFormat.dwBBitMask);
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+
+
+//============================================================
+// monitor_enum_callback
+//============================================================
+
+static BOOL WINAPI monitor_enum_callback(GUID FAR *guid, LPSTR description, LPSTR name, LPVOID context, HMONITOR hmonitor)
+{
+ monitor_enum_info *einfo = (monitor_enum_info *)context;
+
+ // do we match the desired monitor?
+ if (hmonitor == einfo->monitor->handle || (hmonitor == NULL && (einfo->monitor->info.dwFlags & MONITORINFOF_PRIMARY) != 0))
+ {
+ einfo->guid_ptr = (guid != NULL) ? &einfo->guid : NULL;
+ if (guid != NULL)
+ einfo->guid = *guid;
+ einfo->foundit = TRUE;
+ }
+ return 1;
+}
+
+
+
+//============================================================
+// get_adapter_for_monitor
+//============================================================
+
+static void get_adapter_for_monitor(dd_info *dd, win_monitor_info *monitor)
+{
+ monitor_enum_info einfo;
+ HRESULT result;
+
+ // try to find our monitor
+ memset(&einfo, 0, sizeof(einfo));
+ einfo.monitor = monitor;
+ result = (*directdrawenumerateex)(monitor_enum_callback, &einfo, DDENUM_ATTACHEDSECONDARYDEVICES);
+ if (result != DD_OK) mame_printf_verbose("DirectDraw: Error %08X during DirectDrawEnumerateEx call\n", (int)result);
+
+ // set up the adapter
+ if (einfo.foundit && einfo.guid_ptr != NULL)
+ {
+ dd->adapter = einfo.guid;
+ dd->adapter_ptr = &dd->adapter;
+ }
+ else
+ dd->adapter_ptr = NULL;
+}
+
+
+
+//============================================================
+// pick_best_mode
+//============================================================
+
+static HRESULT WINAPI enum_modes_callback(LPDDSURFACEDESC2 desc, LPVOID context)
+{
+ float size_score, refresh_score, final_score;
+ mode_enum_info *einfo = context;
+ dd_info *dd = einfo->window->drawdata;
+
+ // skip non-32 bit modes
+ if (desc->ddpfPixelFormat.dwRGBBitCount != 32)
+ return DDENUMRET_OK;
+
+ // compute initial score based on difference between target and current
+ size_score = 1.0f / (1.0f + fabs((INT32)desc->dwWidth - einfo->target_width) + fabs((INT32)desc->dwHeight - einfo->target_height));
+
+ // if the mode is too small, give a big penalty
+ if (desc->dwWidth < einfo->minimum_width || desc->dwHeight < einfo->minimum_height)
+ size_score *= 0.01f;
+
+ // if mode is smaller than we'd like, it only scores up to 0.1
+ if (desc->dwWidth < einfo->target_width || desc->dwHeight < einfo->target_height)
+ size_score *= 0.1f;
+
+ // if we're looking for a particular mode, that's a winner
+ if (desc->dwWidth == einfo->window->maxwidth && desc->dwHeight == einfo->window->maxheight)
+ size_score = 2.0f;
+
+ // compute refresh score
+ refresh_score = 1.0f / (1.0f + fabs((double)desc->dwRefreshRate - einfo->target_refresh));
+
+ // if refresh is smaller than we'd like, it only scores up to 0.1
+ if ((double)desc->dwRefreshRate < einfo->target_refresh)
+ refresh_score *= 0.1;
+
+ // if we're looking for a particular refresh, make sure it matches
+ if (desc->dwRefreshRate == einfo->window->refresh)
+ refresh_score = 2.0f;
+
+ // weight size and refresh equally
+ final_score = size_score + refresh_score;
+
+ // best so far?
+ mame_printf_verbose(" %4dx%4d@%3dHz -> %f\n", (int)desc->dwWidth, (int)desc->dwHeight, (int)desc->dwRefreshRate, final_score * 1000.0f);
+ if (final_score > einfo->best_score)
+ {
+ einfo->best_score = final_score;
+ dd->width = desc->dwWidth;
+ dd->height = desc->dwHeight;
+ dd->refresh = desc->dwRefreshRate;
+ }
+ return DDENUMRET_OK;
+}
+
+
+
+//============================================================
+// pick_best_mode
+//============================================================
+
+static void pick_best_mode(win_window_info *window)
+{
+ dd_info *dd = window->drawdata;
+ mode_enum_info einfo;
+ HRESULT result;
+
+ // determine the minimum width/height for the selected target
+ // note: technically we should not be calling this from an alternate window
+ // thread; however, it is only done during init time, and the init code on
+ // the main thread is waiting for us to finish, so it is safe to do so here
+ render_target_get_minimum_size(window->target, &einfo.minimum_width, &einfo.minimum_height);
+
+ // use those as the target for now
+ einfo.target_width = einfo.minimum_width * MAX(1, video_config.prescale);
+ einfo.target_height = einfo.minimum_height * MAX(1, video_config.prescale);
+ einfo.target_refresh = ATTOSECONDS_TO_HZ(Machine->screen[0].refresh);
+
+ // if we're not stretching, allow some slop on the minimum since we can handle it
+ if (!video_config.hwstretch)
+ {
+ einfo.minimum_width -= 4;
+ einfo.minimum_height -= 4;
+ }
+
+ // if we are stretching, aim for a mode approximately 2x the game's resolution
+ else if (video_config.prescale <= 1)
+ {
+ einfo.target_width *= 2;
+ einfo.target_height *= 2;
+ }
+
+ // fill in the rest of the data
+ einfo.window = window;
+ einfo.best_score = 0.0f;
+
+ // enumerate the modes
+ mame_printf_verbose("DirectDraw: Selecting video mode...\n");
+ result = IDirectDraw7_EnumDisplayModes(dd->ddraw, DDEDM_REFRESHRATES, NULL, &einfo, enum_modes_callback);
+ if (result != DD_OK) mame_printf_verbose("DirectDraw: Error %08X during EnumDisplayModes call\n", (int)result);
+ mame_printf_verbose("DirectDraw: Mode selected = %4dx%4d@%3dHz\n", dd->width, dd->height, dd->refresh);
+}
+
+
+
+//============================================================
+// SOFTWARE RENDERING
+//============================================================
+
+#define FUNC_PREFIX(x) drawdd_rgb888_##x
+#define PIXEL_TYPE UINT32
+#define SRCSHIFT_R 0
+#define SRCSHIFT_G 0
+#define SRCSHIFT_B 0
+#define DSTSHIFT_R 16
+#define DSTSHIFT_G 8
+#define DSTSHIFT_B 0
+
+#include "rendersw.c"
+
+#define FUNC_PREFIX(x) drawdd_bgr888_##x
+#define PIXEL_TYPE UINT32
+#define SRCSHIFT_R 0
+#define SRCSHIFT_G 0
+#define SRCSHIFT_B 0
+#define DSTSHIFT_R 0
+#define DSTSHIFT_G 8
+#define DSTSHIFT_B 16
+
+#include "rendersw.c"
+
+#define FUNC_PREFIX(x) drawdd_rgb565_##x
+#define PIXEL_TYPE UINT16
+#define SRCSHIFT_R 3
+#define SRCSHIFT_G 2
+#define SRCSHIFT_B 3
+#define DSTSHIFT_R 11
+#define DSTSHIFT_G 5
+#define DSTSHIFT_B 0
+
+#include "rendersw.c"
+
+#define FUNC_PREFIX(x) drawdd_rgb555_##x
+#define PIXEL_TYPE UINT16
+#define SRCSHIFT_R 3
+#define SRCSHIFT_G 3
+#define SRCSHIFT_B 3
+#define DSTSHIFT_R 10
+#define DSTSHIFT_G 5
+#define DSTSHIFT_B 0
+
+#include "rendersw.c"
+
+
+
+//============================================================
+// SOFTWARE RENDERING -- NO READING VARIANTS
+//============================================================
+
+#define FUNC_PREFIX(x) drawdd_rgb888_nr_##x
+#define PIXEL_TYPE UINT32
+#define SRCSHIFT_R 0
+#define SRCSHIFT_G 0
+#define SRCSHIFT_B 0
+#define DSTSHIFT_R 16
+#define DSTSHIFT_G 8
+#define DSTSHIFT_B 0
+#define NO_DEST_READ 1
+
+#include "rendersw.c"
+
+#define FUNC_PREFIX(x) drawdd_bgr888_nr_##x
+#define PIXEL_TYPE UINT32
+#define SRCSHIFT_R 0
+#define SRCSHIFT_G 0
+#define SRCSHIFT_B 0
+#define DSTSHIFT_R 0
+#define DSTSHIFT_G 8
+#define DSTSHIFT_B 16
+#define NO_DEST_READ 1
+
+#include "rendersw.c"
+
+#define FUNC_PREFIX(x) drawdd_rgb565_nr_##x
+#define PIXEL_TYPE UINT16
+#define SRCSHIFT_R 3
+#define SRCSHIFT_G 2
+#define SRCSHIFT_B 3
+#define DSTSHIFT_R 11
+#define DSTSHIFT_G 5
+#define DSTSHIFT_B 0
+#define NO_DEST_READ 1
+
+#include "rendersw.c"
+
+#define FUNC_PREFIX(x) drawdd_rgb555_nr_##x
+#define PIXEL_TYPE UINT16
+#define SRCSHIFT_R 3
+#define SRCSHIFT_G 3
+#define SRCSHIFT_B 3
+#define DSTSHIFT_R 10
+#define DSTSHIFT_G 5
+#define DSTSHIFT_B 0
+#define NO_DEST_READ 1
+
+#include "rendersw.c"
diff --git a/src/osd/windows/drawgdi.c b/src/osd/windows/drawgdi.c
new file mode 100644
index 00000000000..05f1138c11f
--- /dev/null
+++ b/src/osd/windows/drawgdi.c
@@ -0,0 +1,215 @@
+//============================================================
+//
+// drawgdi.c - Win32 GDI drawing
+//
+// Copyright (c) 1996-2007, Nicola Salmoria and the MAME Team.
+// Visit http://mamedev.org for licensing and usage restrictions.
+//
+//============================================================
+
+// standard windows headers
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+
+// MAME headers
+#include "mamecore.h"
+
+// MAMEOS headers
+#include "window.h"
+
+
+
+//============================================================
+// TYPE DEFINITIONS
+//============================================================
+
+/* gdi_info is the information for the current screen */
+typedef struct _gdi_info gdi_info;
+struct _gdi_info
+{
+ BITMAPINFO bminfo;
+ RGBQUAD colors[256];
+ UINT8 * bmdata;
+ size_t bmsize;
+};
+
+
+
+//============================================================
+// PROTOTYPES
+//============================================================
+
+// core functions
+static void drawgdi_exit(void);
+static int drawgdi_window_init(win_window_info *window);
+static void drawgdi_window_destroy(win_window_info *window);
+static const render_primitive_list *drawgdi_window_get_primitives(win_window_info *window);
+static int drawgdi_window_draw(win_window_info *window, HDC dc, int update);
+
+// rendering
+static void drawgdi_rgb888_draw_primitives(const render_primitive *primlist, void *dstdata, UINT32 width, UINT32 height, UINT32 pitch);
+
+
+
+//============================================================
+// drawgdi_init
+//============================================================
+
+int drawgdi_init(win_draw_callbacks *callbacks)
+{
+ // fill in the callbacks
+ callbacks->exit = drawgdi_exit;
+ callbacks->window_init = drawgdi_window_init;
+ callbacks->window_get_primitives = drawgdi_window_get_primitives;
+ callbacks->window_draw = drawgdi_window_draw;
+ callbacks->window_destroy = drawgdi_window_destroy;
+ return 0;
+}
+
+
+
+//============================================================
+// drawgdi_exit
+//============================================================
+
+static void drawgdi_exit(void)
+{
+}
+
+
+
+//============================================================
+// drawgdi_window_init
+//============================================================
+
+static int drawgdi_window_init(win_window_info *window)
+{
+ gdi_info *gdi;
+ int i;
+
+ // allocate memory for our structures
+ gdi = malloc_or_die(sizeof(*gdi));
+ memset(gdi, 0, sizeof(*gdi));
+ window->drawdata = gdi;
+
+ // fill in the bitmap info header
+ gdi->bminfo.bmiHeader.biSize = sizeof(gdi->bminfo.bmiHeader);
+ gdi->bminfo.bmiHeader.biPlanes = 1;
+ gdi->bminfo.bmiHeader.biCompression = BI_RGB;
+ gdi->bminfo.bmiHeader.biSizeImage = 0;
+ gdi->bminfo.bmiHeader.biXPelsPerMeter = 0;
+ gdi->bminfo.bmiHeader.biYPelsPerMeter = 0;
+ gdi->bminfo.bmiHeader.biClrUsed = 0;
+ gdi->bminfo.bmiHeader.biClrImportant = 0;
+
+ // initialize the palette to a gray ramp
+ for (i = 0; i < 256; i++)
+ {
+ gdi->bminfo.bmiColors[i].rgbRed = i;
+ gdi->bminfo.bmiColors[i].rgbGreen = i;
+ gdi->bminfo.bmiColors[i].rgbBlue = i;
+ gdi->bminfo.bmiColors[i].rgbReserved = i;
+ }
+
+ return 0;
+}
+
+
+
+//============================================================
+// drawgdi_window_destroy
+//============================================================
+
+static void drawgdi_window_destroy(win_window_info *window)
+{
+ gdi_info *gdi = window->drawdata;
+
+ // skip if nothing
+ if (gdi == NULL)
+ return;
+
+ // free the bitmap memory
+ if (gdi->bmdata != NULL)
+ free(gdi->bmdata);
+ free(gdi);
+ window->drawdata = NULL;
+}
+
+
+
+//============================================================
+// drawgdi_window_get_primitives
+//============================================================
+
+static const render_primitive_list *drawgdi_window_get_primitives(win_window_info *window)
+{
+ RECT client;
+ GetClientRect(window->hwnd, &client);
+ render_target_set_bounds(window->target, rect_width(&client), rect_height(&client), winvideo_monitor_get_aspect(window->monitor));
+ return render_target_get_primitives(window->target);
+}
+
+
+
+//============================================================
+// drawgdi_window_draw
+//============================================================
+
+static int drawgdi_window_draw(win_window_info *window, HDC dc, int update)
+{
+ gdi_info *gdi = window->drawdata;
+ int width, height, pitch;
+ RECT bounds;
+
+ // we don't have any special resize behaviors
+ if (window->resize_state == RESIZE_STATE_PENDING)
+ window->resize_state = RESIZE_STATE_NORMAL;
+
+ // get the target bounds
+ GetClientRect(window->hwnd, &bounds);
+
+ // compute width/height/pitch of target
+ width = rect_width(&bounds);
+ height = rect_height(&bounds);
+ pitch = (width + 3) & ~3;
+
+ // make sure our temporary bitmap is big enough
+ if (pitch * height * 4 > gdi->bmsize)
+ {
+ gdi->bmsize = pitch * height * 4 * 2;
+ gdi->bmdata = realloc(gdi->bmdata, gdi->bmsize);
+ }
+
+ // draw the primitives to the bitmap
+ osd_lock_acquire(window->primlist->lock);
+ drawgdi_rgb888_draw_primitives(window->primlist->head, gdi->bmdata, width, height, pitch);
+ osd_lock_release(window->primlist->lock);
+
+ // fill in bitmap-specific info
+ gdi->bminfo.bmiHeader.biWidth = pitch;
+ gdi->bminfo.bmiHeader.biHeight = -height;
+ gdi->bminfo.bmiHeader.biBitCount = 32;
+
+ // blit to the screen
+ StretchDIBits(dc, 0, 0, width, height,
+ 0, 0, width, height,
+ gdi->bmdata, &gdi->bminfo, DIB_RGB_COLORS, SRCCOPY);
+ return 0;
+}
+
+
+
+//============================================================
+// SOFTWARE RENDERING
+//============================================================
+
+#define FUNC_PREFIX(x) drawgdi_rgb888_##x
+#define PIXEL_TYPE UINT32
+#define SRCSHIFT_R 0
+#define SRCSHIFT_G 0
+#define SRCSHIFT_B 0
+#define DSTSHIFT_R 16
+#define DSTSHIFT_G 8
+#define DSTSHIFT_B 0
+
+#include "rendersw.c"
diff --git a/src/osd/windows/drawnone.c b/src/osd/windows/drawnone.c
new file mode 100644
index 00000000000..b2f84d8d50e
--- /dev/null
+++ b/src/osd/windows/drawnone.c
@@ -0,0 +1,104 @@
+//============================================================
+//
+// drawnone.c - stub "nothing" drawer
+//
+// Copyright (c) 1996-2007, Nicola Salmoria and the MAME Team.
+// Visit http://mamedev.org for licensing and usage restrictions.
+//
+//============================================================
+
+// standard windows headers
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+
+// MAME headers
+#include "mamecore.h"
+
+// MAMEOS headers
+#include "window.h"
+
+
+
+//============================================================
+// PROTOTYPES
+//============================================================
+
+// core functions
+static void drawnone_exit(void);
+static int drawnone_window_init(win_window_info *window);
+static void drawnone_window_destroy(win_window_info *window);
+static const render_primitive_list *drawnone_window_get_primitives(win_window_info *window);
+static int drawnone_window_draw(win_window_info *window, HDC dc, int update);
+
+
+
+//============================================================
+// drawnone_init
+//============================================================
+
+int drawnone_init(win_draw_callbacks *callbacks)
+{
+ // fill in the callbacks
+ callbacks->exit = drawnone_exit;
+ callbacks->window_init = drawnone_window_init;
+ callbacks->window_get_primitives = drawnone_window_get_primitives;
+ callbacks->window_draw = drawnone_window_draw;
+ callbacks->window_destroy = drawnone_window_destroy;
+ return 0;
+}
+
+
+
+//============================================================
+// drawnone_exit
+//============================================================
+
+static void drawnone_exit(void)
+{
+}
+
+
+
+//============================================================
+// drawnone_window_init
+//============================================================
+
+static int drawnone_window_init(win_window_info *window)
+{
+ return 0;
+}
+
+
+
+//============================================================
+// drawnone_window_destroy
+//============================================================
+
+static void drawnone_window_destroy(win_window_info *window)
+{
+}
+
+
+
+//============================================================
+// drawnone_window_get_primitives
+//============================================================
+
+static const render_primitive_list *drawnone_window_get_primitives(win_window_info *window)
+{
+ RECT client;
+ GetClientRect(window->hwnd, &client);
+ render_target_set_bounds(window->target, rect_width(&client), rect_height(&client), winvideo_monitor_get_aspect(window->monitor));
+ return render_target_get_primitives(window->target);
+}
+
+
+
+//============================================================
+// drawnone_window_draw
+//============================================================
+
+static int drawnone_window_draw(win_window_info *window, HDC dc, int update)
+{
+ return 0;
+}
diff --git a/src/osd/windows/input.c b/src/osd/windows/input.c
new file mode 100644
index 00000000000..42a23c713bf
--- /dev/null
+++ b/src/osd/windows/input.c
@@ -0,0 +1,2129 @@
+//============================================================
+//
+// input.c - Win32 implementation of MAME input routines
+//
+// Copyright (c) 1996-2007, Nicola Salmoria and the MAME Team.
+// Visit http://mamedev.org for licensing and usage restrictions.
+//
+//============================================================
+
+// For testing purposes: force DirectInput
+#define FORCE_DIRECTINPUT 0
+
+// Needed for RAW Input
+#define _WIN32_WINNT 0x501
+
+// standard windows headers
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#include <winioctl.h>
+#include <tchar.h>
+
+// undef WINNT for dinput.h to prevent duplicate definition
+#undef WINNT
+#ifdef DIRECTINPUT_VERSION
+#undef DIRECTINPUT_VERSION
+#endif
+#define DIRECTINPUT_VERSION 0x0700
+#include <dinput.h>
+
+// standard C headers
+#include <conio.h>
+#include <ctype.h>
+#include <stddef.h>
+
+// MAME headers
+#include "osdepend.h"
+#include "driver.h"
+#include "ui.h"
+
+// MAMEOS headers
+#include "winmain.h"
+#include "window.h"
+#include "input.h"
+#include "debugwin.h"
+#include "video.h"
+#include "strconv.h"
+#include "config.h"
+
+#ifdef MESS
+#include "uimess.h"
+#endif
+
+
+
+//============================================================
+// PARAMETERS
+//============================================================
+
+enum
+{
+ POVDIR_LEFT = 0,
+ POVDIR_RIGHT,
+ POVDIR_UP,
+ POVDIR_DOWN
+};
+
+#define MAX_KEYS 256
+
+#define MAME_KEY 0
+#define DI_KEY 1
+#define VIRTUAL_KEY 2
+#define ASCII_KEY 3
+
+
+
+//============================================================
+// MACROS
+//============================================================
+
+#define STRUCTSIZE(x) ((dinput_version == 0x0300) ? sizeof(x##_DX3) : sizeof(x))
+
+#ifdef UNICODE
+#define UNICODE_SUFFIX "W"
+#else
+#define UNICODE_SUFFIX "A"
+#endif
+
+
+
+//============================================================
+// TYPEDEFS
+//============================================================
+
+// state information for a keyboard; DirectInput state must be first element
+typedef struct _keyboard_state keyboard_state;
+struct _keyboard_state
+{
+ UINT8 state[MAX_KEYS];
+ INT8 oldkey[MAX_KEYS];
+ INT8 currkey[MAX_KEYS];
+};
+
+
+// state information for a mouse; DirectInput state must be first element
+typedef struct _mouse_state mouse_state;
+struct _mouse_state
+{
+ DIMOUSESTATE2 state;
+ mouse_state * partner;
+ LONG raw_x, raw_y, raw_z;
+};
+
+
+// state information for a joystick; DirectInput state must be first element
+typedef struct _joystick_state joystick_state;
+struct _joystick_state
+{
+ DIJOYSTATE state;
+ LONG rangemin[8];
+ LONG rangemax[8];
+};
+
+
+// DirectInput-specific information about a device
+typedef struct _dinput_device_info dinput_device_info;
+struct _dinput_device_info
+{
+ LPDIRECTINPUTDEVICE device;
+ LPDIRECTINPUTDEVICE2 device2;
+ DIDEVCAPS caps;
+ LPCDIDATAFORMAT format;
+};
+
+
+// RawInput-specific information about a device
+typedef struct _rawinput_device_info rawinput_device_info;
+struct _rawinput_device_info
+{
+ HANDLE device;
+};
+
+
+// generic device information
+typedef struct _device_info device_info;
+struct _device_info
+{
+ // device information
+ device_info ** head;
+ device_info * next;
+ const char * name;
+ void (*poll)(device_info *info);
+
+ // MAME information
+ input_device * device;
+
+ // device state
+ union
+ {
+ keyboard_state keyboard;
+ mouse_state mouse;
+ joystick_state joystick;
+ };
+
+ // DirectInput/RawInput-specific state
+ dinput_device_info dinput;
+ rawinput_device_info rawinput;
+};
+
+
+// RawInput APIs
+typedef WINUSERAPI INT (WINAPI *get_rawinput_device_list_ptr)(OUT PRAWINPUTDEVICELIST pRawInputDeviceList, IN OUT PINT puiNumDevices, IN UINT cbSize);
+typedef WINUSERAPI INT (WINAPI *get_rawinput_data_ptr)(IN HRAWINPUT hRawInput, IN UINT uiCommand, OUT LPVOID pData, IN OUT PINT pcbSize, IN UINT cbSizeHeader);
+typedef WINUSERAPI INT (WINAPI *get_rawinput_device_info_ptr)(IN HANDLE hDevice, IN UINT uiCommand, OUT LPVOID pData, IN OUT PINT pcbSize);
+typedef WINUSERAPI BOOL (WINAPI *register_rawinput_devices_ptr)(IN PCRAWINPUTDEVICE pRawInputDevices, IN UINT uiNumDevices, IN UINT cbSize);
+
+
+
+//============================================================
+// LOCAL VARIABLES
+//============================================================
+
+// global states
+static UINT8 input_enabled;
+static osd_lock * input_lock;
+static UINT8 input_paused;
+static DWORD last_poll;
+
+// DirectInput variables
+static LPDIRECTINPUT dinput;
+static int dinput_version;
+
+// RawInput variables
+static get_rawinput_device_list_ptr get_rawinput_device_list;
+static get_rawinput_data_ptr get_rawinput_data;
+static get_rawinput_device_info_ptr get_rawinput_device_info;
+static register_rawinput_devices_ptr register_rawinput_devices;
+
+// keyboard states
+static UINT8 keyboard_win32_reported_key_down;
+static device_info * keyboard_list;
+
+// mouse states
+static UINT8 mouse_enabled;
+static device_info * mouse_list;
+
+// lightgun states
+static UINT8 lightgun_shared_axis_mode;
+static UINT8 lightgun_enabled;
+static device_info * lightgun_list;
+
+// joystick states
+static device_info * joystick_list;
+
+// default axis names
+static const TCHAR *default_axis_name[] =
+{
+ TEXT("X"), TEXT("Y"), TEXT("Z"), TEXT("RX"),
+ TEXT("RY"), TEXT("RZ"), TEXT("SL1"), TEXT("SL2")
+};
+
+
+
+//============================================================
+// PROTOTYPES
+//============================================================
+
+static void wininput_pause(running_machine *machine, int paused);
+static void wininput_exit(running_machine *machine);
+
+// device list management
+static void device_list_poll_devices(device_info *devlist_head);
+static void device_list_reset_devices(device_info *devlist_head);
+
+// generic device management
+static device_info *generic_device_alloc(device_info **devlist_head_ptr, const TCHAR *name);
+static void generic_device_free(device_info *devinfo);
+static int generic_device_index(device_info *devlist_head, device_info *devinfo);
+static void generic_device_reset(device_info *devinfo);
+static INT32 generic_button_get_state(void *device_internal, void *item_internal);
+static INT32 generic_axis_get_state(void *device_internal, void *item_internal);
+
+// Win32-specific input code
+static void win32_init(running_machine *machine);
+static void win32_exit(running_machine *machine);
+static void win32_keyboard_poll(device_info *devinfo);
+static void win32_lightgun_poll(device_info *devinfo);
+
+// DirectInput-specific code
+static void dinput_init(running_machine *machine);
+static void dinput_exit(running_machine *machine);
+static HRESULT dinput_set_dword_property(LPDIRECTINPUTDEVICE device, REFGUID property_guid, DWORD object, DWORD how, DWORD value);
+static device_info *dinput_device_create(device_info **devlist_head_ptr, LPCDIDEVICEINSTANCE instance, LPCDIDATAFORMAT format1, LPCDIDATAFORMAT format2, DWORD cooperative_level);
+static void dinput_device_release(device_info *devinfo);
+static const char *dinput_device_item_name(device_info *devinfo, int offset, const TCHAR *defstring, const TCHAR *suffix);
+static HRESULT dinput_device_poll(device_info *devinfo);
+static BOOL CALLBACK dinput_keyboard_enum(LPCDIDEVICEINSTANCE instance, LPVOID ref);
+static void dinput_keyboard_poll(device_info *devinfo);
+static BOOL CALLBACK dinput_mouse_enum(LPCDIDEVICEINSTANCE instance, LPVOID ref);
+static void dinput_mouse_poll(device_info *devinfo);
+static BOOL CALLBACK dinput_joystick_enum(LPCDIDEVICEINSTANCE instance, LPVOID ref);
+static void dinput_joystick_poll(device_info *devinfo);
+static INT32 dinput_joystick_pov_get_state(void *device_internal, void *item_internal);
+
+// RawInput-specific code
+static void rawinput_init(running_machine *machine);
+static void rawinput_exit(running_machine *machine);
+static device_info *rawinput_device_create(device_info **devlist_head_ptr, PRAWINPUTDEVICELIST device);
+static void rawinput_device_release(device_info *info);
+static TCHAR *rawinput_device_improve_name(TCHAR *name);
+static void rawinput_keyboard_enum(PRAWINPUTDEVICELIST device);
+static void rawinput_keyboard_update(HANDLE device, RAWKEYBOARD *data);
+static void rawinput_mouse_enum(PRAWINPUTDEVICELIST device);
+static void rawinput_mouse_update(HANDLE device, RAWMOUSE *data);
+static void rawinput_mouse_poll(device_info *devinfo);
+
+// misc utilities
+static TCHAR *reg_query_string(HKEY key, const TCHAR *path);
+static const TCHAR *default_button_name(int which);
+static const TCHAR *default_pov_name(int which);
+
+
+
+//============================================================
+// KEYBOARD/JOYSTICK LIST
+//============================================================
+
+// master keyboard translation table
+static const int win_key_trans_table[][4] =
+{
+ // MAME key dinput key virtual key ascii
+ { ITEM_ID_ESC, DIK_ESCAPE, VK_ESCAPE, 27 },
+ { ITEM_ID_1, DIK_1, '1', '1' },
+ { ITEM_ID_2, DIK_2, '2', '2' },
+ { ITEM_ID_3, DIK_3, '3', '3' },
+ { ITEM_ID_4, DIK_4, '4', '4' },
+ { ITEM_ID_5, DIK_5, '5', '5' },
+ { ITEM_ID_6, DIK_6, '6', '6' },
+ { ITEM_ID_7, DIK_7, '7', '7' },
+ { ITEM_ID_8, DIK_8, '8', '8' },
+ { ITEM_ID_9, DIK_9, '9', '9' },
+ { ITEM_ID_0, DIK_0, '0', '0' },
+ { ITEM_ID_MINUS, DIK_MINUS, VK_OEM_MINUS, '-' },
+ { ITEM_ID_EQUALS, DIK_EQUALS, VK_OEM_PLUS, '=' },
+ { ITEM_ID_BACKSPACE, DIK_BACK, VK_BACK, 8 },
+ { ITEM_ID_TAB, DIK_TAB, VK_TAB, 9 },
+ { ITEM_ID_Q, DIK_Q, 'Q', 'Q' },
+ { ITEM_ID_W, DIK_W, 'W', 'W' },
+ { ITEM_ID_E, DIK_E, 'E', 'E' },
+ { ITEM_ID_R, DIK_R, 'R', 'R' },
+ { ITEM_ID_T, DIK_T, 'T', 'T' },
+ { ITEM_ID_Y, DIK_Y, 'Y', 'Y' },
+ { ITEM_ID_U, DIK_U, 'U', 'U' },
+ { ITEM_ID_I, DIK_I, 'I', 'I' },
+ { ITEM_ID_O, DIK_O, 'O', 'O' },
+ { ITEM_ID_P, DIK_P, 'P', 'P' },
+ { ITEM_ID_OPENBRACE, DIK_LBRACKET, VK_OEM_4, '[' },
+ { ITEM_ID_CLOSEBRACE, DIK_RBRACKET, VK_OEM_6, ']' },
+ { ITEM_ID_ENTER, DIK_RETURN, VK_RETURN, 13 },
+ { ITEM_ID_LCONTROL, DIK_LCONTROL, VK_LCONTROL, 0 },
+ { ITEM_ID_A, DIK_A, 'A', 'A' },
+ { ITEM_ID_S, DIK_S, 'S', 'S' },
+ { ITEM_ID_D, DIK_D, 'D', 'D' },
+ { ITEM_ID_F, DIK_F, 'F', 'F' },
+ { ITEM_ID_G, DIK_G, 'G', 'G' },
+ { ITEM_ID_H, DIK_H, 'H', 'H' },
+ { ITEM_ID_J, DIK_J, 'J', 'J' },
+ { ITEM_ID_K, DIK_K, 'K', 'K' },
+ { ITEM_ID_L, DIK_L, 'L', 'L' },
+ { ITEM_ID_COLON, DIK_SEMICOLON, VK_OEM_1, ';' },
+ { ITEM_ID_QUOTE, DIK_APOSTROPHE, VK_OEM_7, '\'' },
+ { ITEM_ID_TILDE, DIK_GRAVE, VK_OEM_3, '`' },
+ { ITEM_ID_LSHIFT, DIK_LSHIFT, VK_LSHIFT, 0 },
+ { ITEM_ID_BACKSLASH, DIK_BACKSLASH, VK_OEM_5, '\\' },
+ { ITEM_ID_Z, DIK_Z, 'Z', 'Z' },
+ { ITEM_ID_X, DIK_X, 'X', 'X' },
+ { ITEM_ID_C, DIK_C, 'C', 'C' },
+ { ITEM_ID_V, DIK_V, 'V', 'V' },
+ { ITEM_ID_B, DIK_B, 'B', 'B' },
+ { ITEM_ID_N, DIK_N, 'N', 'N' },
+ { ITEM_ID_M, DIK_M, 'M', 'M' },
+ { ITEM_ID_COMMA, DIK_COMMA, VK_OEM_COMMA, ',' },
+ { ITEM_ID_STOP, DIK_PERIOD, VK_OEM_PERIOD, '.' },
+ { ITEM_ID_SLASH, DIK_SLASH, VK_OEM_2, '/' },
+ { ITEM_ID_RSHIFT, DIK_RSHIFT, VK_RSHIFT, 0 },
+ { ITEM_ID_ASTERISK, DIK_MULTIPLY, VK_MULTIPLY, '*' },
+ { ITEM_ID_LALT, DIK_LMENU, VK_LMENU, 0 },
+ { ITEM_ID_SPACE, DIK_SPACE, VK_SPACE, ' ' },
+ { ITEM_ID_CAPSLOCK, DIK_CAPITAL, VK_CAPITAL, 0 },
+ { ITEM_ID_F1, DIK_F1, VK_F1, 0 },
+ { ITEM_ID_F2, DIK_F2, VK_F2, 0 },
+ { ITEM_ID_F3, DIK_F3, VK_F3, 0 },
+ { ITEM_ID_F4, DIK_F4, VK_F4, 0 },
+ { ITEM_ID_F5, DIK_F5, VK_F5, 0 },
+ { ITEM_ID_F6, DIK_F6, VK_F6, 0 },
+ { ITEM_ID_F7, DIK_F7, VK_F7, 0 },
+ { ITEM_ID_F8, DIK_F8, VK_F8, 0 },
+ { ITEM_ID_F9, DIK_F9, VK_F9, 0 },
+ { ITEM_ID_F10, DIK_F10, VK_F10, 0 },
+ { ITEM_ID_NUMLOCK, DIK_NUMLOCK, VK_NUMLOCK, 0 },
+ { ITEM_ID_SCRLOCK, DIK_SCROLL, VK_SCROLL, 0 },
+ { ITEM_ID_7_PAD, DIK_NUMPAD7, VK_NUMPAD7, 0 },
+ { ITEM_ID_8_PAD, DIK_NUMPAD8, VK_NUMPAD8, 0 },
+ { ITEM_ID_9_PAD, DIK_NUMPAD9, VK_NUMPAD9, 0 },
+ { ITEM_ID_MINUS_PAD, DIK_SUBTRACT, VK_SUBTRACT, 0 },
+ { ITEM_ID_4_PAD, DIK_NUMPAD4, VK_NUMPAD4, 0 },
+ { ITEM_ID_5_PAD, DIK_NUMPAD5, VK_NUMPAD5, 0 },
+ { ITEM_ID_6_PAD, DIK_NUMPAD6, VK_NUMPAD6, 0 },
+ { ITEM_ID_PLUS_PAD, DIK_ADD, VK_ADD, 0 },
+ { ITEM_ID_1_PAD, DIK_NUMPAD1, VK_NUMPAD1, 0 },
+ { ITEM_ID_2_PAD, DIK_NUMPAD2, VK_NUMPAD2, 0 },
+ { ITEM_ID_3_PAD, DIK_NUMPAD3, VK_NUMPAD3, 0 },
+ { ITEM_ID_0_PAD, DIK_NUMPAD0, VK_NUMPAD0, 0 },
+ { ITEM_ID_DEL_PAD, DIK_DECIMAL, VK_DECIMAL, 0 },
+ { ITEM_ID_F11, DIK_F11, VK_F11, 0 },
+ { ITEM_ID_F12, DIK_F12, VK_F12, 0 },
+ { ITEM_ID_F13, DIK_F13, VK_F13, 0 },
+ { ITEM_ID_F14, DIK_F14, VK_F14, 0 },
+ { ITEM_ID_F15, DIK_F15, VK_F15, 0 },
+ { ITEM_ID_ENTER_PAD, DIK_NUMPADENTER, VK_RETURN, 0 },
+ { ITEM_ID_RCONTROL, DIK_RCONTROL, VK_RCONTROL, 0 },
+ { ITEM_ID_SLASH_PAD, DIK_DIVIDE, VK_DIVIDE, 0 },
+ { ITEM_ID_PRTSCR, DIK_SYSRQ, 0, 0 },
+ { ITEM_ID_RALT, DIK_RMENU, VK_RMENU, 0 },
+ { ITEM_ID_HOME, DIK_HOME, VK_HOME, 0 },
+ { ITEM_ID_UP, DIK_UP, VK_UP, 0 },
+ { ITEM_ID_PGUP, DIK_PRIOR, VK_PRIOR, 0 },
+ { ITEM_ID_LEFT, DIK_LEFT, VK_LEFT, 0 },
+ { ITEM_ID_RIGHT, DIK_RIGHT, VK_RIGHT, 0 },
+ { ITEM_ID_END, DIK_END, VK_END, 0 },
+ { ITEM_ID_DOWN, DIK_DOWN, VK_DOWN, 0 },
+ { ITEM_ID_PGDN, DIK_NEXT, VK_NEXT, 0 },
+ { ITEM_ID_INSERT, DIK_INSERT, VK_INSERT, 0 },
+ { ITEM_ID_DEL, DIK_DELETE, VK_DELETE, 0 },
+ { ITEM_ID_LWIN, DIK_LWIN, VK_LWIN, 0 },
+ { ITEM_ID_RWIN, DIK_RWIN, VK_RWIN, 0 },
+ { ITEM_ID_MENU, DIK_APPS, VK_APPS, 0 },
+ { ITEM_ID_PAUSE, DIK_PAUSE, VK_PAUSE, 0 },
+ { ITEM_ID_CANCEL, 0, VK_CANCEL, 0 },
+
+ // New keys introduced in Windows 2000. These have no MAME codes to
+ // preserve compatibility with old config files that may refer to them
+ // as e.g. FORWARD instead of e.g. KEYCODE_WEBFORWARD. They need table
+ // entries anyway because otherwise they aren't recognized when
+ // GetAsyncKeyState polling is used (as happens currently when MAME is
+ // paused). Some codes are missing because the mapping to vkey codes
+ // isn't clear, and MapVirtualKey is no help.
+
+ { ITEM_ID_OTHER_SWITCH, DIK_MUTE, VK_VOLUME_MUTE, 0 },
+ { ITEM_ID_OTHER_SWITCH, DIK_VOLUMEDOWN, VK_VOLUME_DOWN, 0 },
+ { ITEM_ID_OTHER_SWITCH, DIK_VOLUMEUP, VK_VOLUME_UP, 0 },
+ { ITEM_ID_OTHER_SWITCH, DIK_WEBHOME, VK_BROWSER_HOME, 0 },
+ { ITEM_ID_OTHER_SWITCH, DIK_WEBSEARCH, VK_BROWSER_SEARCH, 0 },
+ { ITEM_ID_OTHER_SWITCH, DIK_WEBFAVORITES, VK_BROWSER_FAVORITES, 0 },
+ { ITEM_ID_OTHER_SWITCH, DIK_WEBREFRESH, VK_BROWSER_REFRESH, 0 },
+ { ITEM_ID_OTHER_SWITCH, DIK_WEBSTOP, VK_BROWSER_STOP, 0 },
+ { ITEM_ID_OTHER_SWITCH, DIK_WEBFORWARD, VK_BROWSER_FORWARD, 0 },
+ { ITEM_ID_OTHER_SWITCH, DIK_WEBBACK, VK_BROWSER_BACK, 0 },
+ { ITEM_ID_OTHER_SWITCH, DIK_MAIL, VK_LAUNCH_MAIL, 0 },
+ { ITEM_ID_OTHER_SWITCH, DIK_MEDIASELECT, VK_LAUNCH_MEDIA_SELECT, 0 },
+};
+
+
+
+//============================================================
+// INLINE FUNCTIONS
+//============================================================
+
+INLINE void poll_if_necessary(void)
+{
+ // make sure we poll at least once every 1/4 second
+ if (GetTickCount() > last_poll + 1000 / 4)
+ wininput_poll();
+}
+
+
+INLINE input_item_id keyboard_map_scancode_to_itemid(int scancode)
+{
+ int tablenum;
+
+ // scan the table for a match
+ for (tablenum = 0; tablenum < ARRAY_LENGTH(win_key_trans_table); tablenum++)
+ if (win_key_trans_table[tablenum][DI_KEY] == scancode)
+ return win_key_trans_table[tablenum][MAME_KEY];
+
+ // default to an "other" switch
+ return ITEM_ID_OTHER_SWITCH;
+}
+
+
+INLINE INT32 normalize_absolute_axis(INT32 raw, INT32 rawmin, INT32 rawmax)
+{
+ INT32 center = (rawmax + rawmin) / 2;
+
+ // make sure we have valid data
+ if (rawmin >= rawmax)
+ return raw;
+
+ // above center
+ if (raw >= center)
+ {
+ INT32 result = (INT64)(raw - center) * (INT64)INPUT_ABSOLUTE_MAX / (INT64)(rawmax - center);
+ return MIN(result, INPUT_ABSOLUTE_MAX);
+ }
+
+ // below center
+ else
+ {
+ INT32 result = -((INT64)(center - raw) * (INT64)-INPUT_ABSOLUTE_MIN / (INT64)(center - rawmin));
+ return MAX(result, INPUT_ABSOLUTE_MIN);
+ }
+}
+
+
+
+//============================================================
+// wininput_init
+//============================================================
+
+void wininput_init(running_machine *machine)
+{
+ // we need pause and exit callbacks
+ add_pause_callback(machine, wininput_pause);
+ add_exit_callback(machine, wininput_exit);
+
+ // allocate a lock for input synchronizations, since messages sometimes come from another thread
+ input_lock = osd_lock_alloc();
+ assert_always(input_lock != NULL, "Failed to allocate input_lock");
+
+ // decode the options
+ lightgun_shared_axis_mode = options_get_bool(mame_options(), WINOPTION_DUAL_LIGHTGUN);
+
+ // initialize RawInput and DirectInput (RawInput first so we can fall back)
+ rawinput_init(machine);
+ dinput_init(machine);
+ win32_init(machine);
+
+ // poll once to get the initial states
+ input_enabled = TRUE;
+ wininput_poll();
+}
+
+
+//============================================================
+// wininput_pause
+//============================================================
+
+static void wininput_pause(running_machine *machine, int paused)
+{
+ // keep track of the paused state
+ input_paused = paused;
+}
+
+
+//============================================================
+// wininput_exit
+//============================================================
+
+static void wininput_exit(running_machine *machine)
+{
+ // acquire the lock and turn off input (this ensures everyone is done)
+ osd_lock_acquire(input_lock);
+ input_enabled = FALSE;
+ osd_lock_release(input_lock);
+
+ // free the lock
+ osd_lock_free(input_lock);
+}
+
+
+//============================================================
+// wininput_poll
+//============================================================
+
+void wininput_poll(void)
+{
+ int hasfocus = winwindow_has_focus();
+
+ // ignore if not enabled
+ if (!input_enabled)
+ return;
+
+ // remember when this happened
+ last_poll = GetTickCount();
+
+ // periodically process events, in case they're not coming through
+ // this also will make sure the mouse state is up-to-date
+ winwindow_process_events_periodic();
+
+ // track if mouse/lightgun is enabled, for mouse hiding purposes
+ mouse_enabled = input_device_class_enabled(DEVICE_CLASS_MOUSE);
+ lightgun_enabled = input_device_class_enabled(DEVICE_CLASS_LIGHTGUN);
+
+ // poll all of the devices
+ if (hasfocus)
+ {
+ device_list_poll_devices(keyboard_list);
+ device_list_poll_devices(mouse_list);
+ device_list_poll_devices(lightgun_list);
+ device_list_poll_devices(joystick_list);
+ }
+ else
+ {
+ device_list_reset_devices(keyboard_list);
+ device_list_reset_devices(mouse_list);
+ device_list_reset_devices(lightgun_list);
+ device_list_reset_devices(joystick_list);
+ }
+}
+
+
+//============================================================
+// wininput_should_hide_mouse
+//============================================================
+
+int wininput_should_hide_mouse(void)
+{
+ // if we are paused or disabled, no
+ if (input_paused || !input_enabled)
+ return FALSE;
+
+ // if neither mice nor lightguns enabled in the core, then no
+ if (!mouse_enabled && !lightgun_enabled)
+ return FALSE;
+
+ // if the window has a menu, no
+ if (win_window_list != NULL && win_has_menu(win_window_list))
+ return FALSE;
+
+ // otherwise, yes
+ return TRUE;
+}
+
+
+//============================================================
+// wininput_handle_mouse_button
+//============================================================
+
+void wininput_handle_mouse_button(int button, int down, int x, int y)
+{
+ device_info *devinfo;
+
+ // ignore if not enabled
+ if (!input_enabled)
+ return;
+
+ // only need this for shared axis hack
+ if (!lightgun_shared_axis_mode || button >= 4)
+ return;
+
+ // choose a device based on the button
+ devinfo = lightgun_list;
+ if (button >= 2 && devinfo != NULL)
+ {
+ button -= 2;
+ devinfo = devinfo->next;
+ }
+
+ // take the lock
+ osd_lock_acquire(input_lock);
+
+ // set the button state
+ devinfo->mouse.state.rgbButtons[button] = down ? 0x80 : 0x00;
+ if (down)
+ {
+ RECT client_rect;
+ POINT mousepos;
+
+ // get the position relative to the window
+ GetClientRect(win_window_list->hwnd, &client_rect);
+ mousepos.x = x;
+ mousepos.y = y;
+ ScreenToClient(win_window_list->hwnd, &mousepos);
+
+ // convert to absolute coordinates
+ devinfo->mouse.state.lX = normalize_absolute_axis(mousepos.x, client_rect.left, client_rect.right);
+ devinfo->mouse.state.lY = normalize_absolute_axis(mousepos.y, client_rect.top, client_rect.bottom);
+ }
+
+ // release the lock
+ osd_lock_release(input_lock);
+}
+
+
+//============================================================
+// wininput_handle_raw
+//============================================================
+
+BOOL wininput_handle_raw(HANDLE device)
+{
+ BYTE small_buffer[4096];
+ LPBYTE data = small_buffer;
+ BOOL result = FALSE;
+ int size;
+
+ // ignore if not enabled
+ if (!input_enabled)
+ return result;
+
+ // determine the size of databuffer we need
+ if ((*get_rawinput_data)((HRAWINPUT)device, RID_INPUT, NULL, &size, sizeof(RAWINPUTHEADER)) != 0)
+ return result;
+
+ // if necessary, allocate a temporary buffer and fetch the data
+ if (size > sizeof(small_buffer))
+ {
+ data = malloc(size);
+ if (data == NULL)
+ return result;
+ }
+
+ // fetch the data and process the appropriate message types
+ result = (*get_rawinput_data)((HRAWINPUT)device, RID_INPUT, data, &size, sizeof(RAWINPUTHEADER));
+ if (result != 0)
+ {
+ RAWINPUT *input = (RAWINPUT *)data;
+
+ // handle keyboard input
+ if (input->header.dwType == RIM_TYPEKEYBOARD)
+ {
+ osd_lock_acquire(input_lock);
+ rawinput_keyboard_update(input->header.hDevice, &input->data.keyboard);
+ osd_lock_release(input_lock);
+ result = TRUE;
+ }
+
+ // handle mouse input
+ else if (input->header.dwType == RIM_TYPEMOUSE)
+ {
+ osd_lock_acquire(input_lock);
+ rawinput_mouse_update(input->header.hDevice, &input->data.mouse);
+ osd_lock_release(input_lock);
+ result = TRUE;
+ }
+ }
+
+ // free the temporary buffer and return the result
+ if (data != small_buffer)
+ free(data);
+ return result;
+}
+
+
+//============================================================
+// osd_customize_inputport_list
+//============================================================
+
+void osd_customize_inputport_list(input_port_default_entry *defaults)
+{
+ input_port_default_entry *idef = defaults;
+
+ // loop over all the defaults
+ for (idef = defaults; idef->type != IPT_END; idef++)
+ switch (idef->type)
+ {
+ // disable the config menu if the ALT key is down
+ // (allows ALT-TAB to switch between windows apps)
+ case IPT_UI_CONFIGURE:
+ input_seq_set_5(&idef->defaultseq, KEYCODE_TAB, SEQCODE_NOT, KEYCODE_LALT, SEQCODE_NOT, KEYCODE_RALT);
+ break;
+
+ // alt-enter for fullscreen
+ case IPT_OSD_1:
+ idef->token = "TOGGLE_FULLSCREEN";
+ idef->name = "Toggle Fullscreen";
+ input_seq_set_2(&idef->defaultseq, KEYCODE_LALT, KEYCODE_ENTER);
+ break;
+
+#ifdef MESS
+ case IPT_OSD_2:
+ if (mess_use_new_ui())
+ {
+ idef->token = "TOGGLE_MENUBAR";
+ idef->name = "Toggle Menubar";
+ input_seq_set_1 (&idef->defaultseq, KEYCODE_SCRLOCK);
+ }
+ break;
+
+ case IPT_UI_THROTTLE:
+ input_seq_set_0(&idef->defaultseq);
+ break;
+#endif /* MESS */
+ }
+}
+
+
+//============================================================
+// device_list_poll_devices
+//============================================================
+
+static void device_list_poll_devices(device_info *devlist_head)
+{
+ device_info *curdev;
+
+ for (curdev = devlist_head; curdev != NULL; curdev = curdev->next)
+ if (curdev->poll != NULL)
+ (*curdev->poll)(curdev);
+}
+
+
+//============================================================
+// device_list_reset_devices
+//============================================================
+
+static void device_list_reset_devices(device_info *devlist_head)
+{
+ device_info *curdev;
+
+ for (curdev = devlist_head; curdev != NULL; curdev = curdev->next)
+ generic_device_reset(curdev);
+}
+
+
+//============================================================
+// generic_device_alloc
+//============================================================
+
+static device_info *generic_device_alloc(device_info **devlist_head_ptr, const TCHAR *name)
+{
+ device_info **curdev_ptr;
+ device_info *devinfo;
+
+ // allocate memory for the device object
+ devinfo = malloc_or_die(sizeof(*devinfo));
+ memset(devinfo, 0, sizeof(*devinfo));
+ devinfo->head = devlist_head_ptr;
+
+ // allocate a UTF8 copy of the name
+ devinfo->name = utf8_from_tstring(name);
+ if (devinfo->name == NULL)
+ goto error;
+
+ // append us to the list
+ for (curdev_ptr = devinfo->head; *curdev_ptr != NULL; curdev_ptr = &(*curdev_ptr)->next) ;
+ *curdev_ptr = devinfo;
+
+ return devinfo;
+
+error:
+ free(devinfo);
+ return NULL;
+}
+
+
+//============================================================
+// generic_device_free
+//============================================================
+
+static void generic_device_free(device_info *devinfo)
+{
+ device_info **curdev_ptr;
+
+ // remove us from the list
+ for (curdev_ptr = devinfo->head; *curdev_ptr != devinfo && *curdev_ptr != NULL; curdev_ptr = &(*curdev_ptr)->next) ;
+ if (*curdev_ptr == devinfo)
+ *curdev_ptr = devinfo->next;
+
+ // free the copy of the name if present
+ if (devinfo->name != NULL)
+ free((void *)devinfo->name);
+ devinfo->name = NULL;
+
+ // and now free the info
+ free(devinfo);
+}
+
+
+//============================================================
+// generic_device_index
+//============================================================
+
+static int generic_device_index(device_info *devlist_head, device_info *devinfo)
+{
+ int index = 0;
+ while (devlist_head != NULL)
+ {
+ if (devlist_head == devinfo)
+ return index;
+ index++;
+ devlist_head = devlist_head->next;
+ }
+ return -1;
+}
+
+
+//============================================================
+// generic_device_reset
+//============================================================
+
+static void generic_device_reset(device_info *devinfo)
+{
+ // keyboard case
+ if (devinfo->head == &keyboard_list)
+ memset(devinfo->keyboard.state, 0, sizeof(devinfo->keyboard.state));
+
+ // mouse/lightgun case
+ else if (devinfo->head == &mouse_list || devinfo->head == &lightgun_list)
+ memset(&devinfo->mouse.state, 0, sizeof(devinfo->mouse.state));
+
+ // joystick case
+ else if (devinfo->head == &joystick_list)
+ {
+ int povnum;
+
+ memset(&devinfo->joystick.state, 0, sizeof(devinfo->joystick.state));
+ for (povnum = 0; povnum < ARRAY_LENGTH(devinfo->joystick.state.rgdwPOV); povnum++)
+ devinfo->joystick.state.rgdwPOV[povnum] = 0xffff;
+ }
+}
+
+
+//============================================================
+// generic_button_get_state
+//============================================================
+
+static INT32 generic_button_get_state(void *device_internal, void *item_internal)
+{
+ BYTE *itemdata = item_internal;
+
+ // return the current state
+ poll_if_necessary();
+ return *itemdata >> 7;
+}
+
+
+//============================================================
+// generic_axis_get_state
+//============================================================
+
+static INT32 generic_axis_get_state(void *device_internal, void *item_internal)
+{
+ LONG *axisdata = item_internal;
+
+ // return the current state
+ poll_if_necessary();
+ return *axisdata;
+}
+
+
+//============================================================
+// win32_init
+//============================================================
+
+static void win32_init(running_machine *machine)
+{
+ int gunnum;
+
+ // we don't need any initialization unless we are using shared axis mode for lightguns
+ if (!lightgun_shared_axis_mode)
+ return;
+
+ // we need an exit callback
+ add_exit_callback(machine, win32_exit);
+
+ // allocate two lightgun devices
+ for (gunnum = 0; gunnum < 2; gunnum++)
+ {
+ static const TCHAR *gun_names[] = { TEXT("Shared Axis Gun 1"), TEXT("Shared Axis Gun 2") };
+ device_info *devinfo;
+ int axisnum, butnum;
+
+ // allocate a device
+ devinfo = generic_device_alloc(&lightgun_list, gun_names[gunnum]);
+ if (devinfo == NULL)
+ break;
+
+ // add the device
+ devinfo->device = input_device_add(DEVICE_CLASS_LIGHTGUN, devinfo->name, devinfo);
+
+ // populate the axes
+ for (axisnum = 0; axisnum < 2; axisnum++)
+ {
+ const char *name = utf8_from_tstring(default_axis_name[axisnum]);
+ input_device_item_add(devinfo->device, name, &devinfo->mouse.state.lX + axisnum, ITEM_ID_XAXIS + axisnum, generic_axis_get_state);
+ free((void *)name);
+ }
+
+ // populate the buttons
+ for (butnum = 0; butnum < 2; butnum++)
+ {
+ const char *name = utf8_from_tstring(default_button_name(butnum));
+ input_device_item_add(devinfo->device, name, &devinfo->mouse.state.rgbButtons[butnum], ITEM_ID_BUTTON1 + butnum, generic_button_get_state);
+ free((void *)name);
+ }
+ }
+}
+
+
+//============================================================
+// win32_exit
+//============================================================
+
+static void win32_exit(running_machine *machine)
+{
+ // skip if we're in shared axis mode
+ if (!lightgun_shared_axis_mode)
+ return;
+
+ // delete the lightgun devices
+ while (lightgun_list != NULL)
+ generic_device_free(lightgun_list);
+}
+
+
+//============================================================
+// win32_keyboard_poll
+//============================================================
+
+static void win32_keyboard_poll(device_info *devinfo)
+{
+ int keynum;
+
+ // clear the flag that says we detected a key down via win32
+ keyboard_win32_reported_key_down = FALSE;
+
+ // reset the keyboard state and then repopulate
+ memset(devinfo->keyboard.state, 0, sizeof(devinfo->keyboard.state));
+
+ // iterate over keys
+ for (keynum = 0; keynum < ARRAY_LENGTH(win_key_trans_table); keynum++)
+ {
+ int vk = win_key_trans_table[keynum][VIRTUAL_KEY];
+ if (vk != 0 && (GetAsyncKeyState(vk) & 0x8000) != 0)
+ {
+ int dik = win_key_trans_table[keynum][DI_KEY];
+
+ // conver the VK code to a scancode (DIK code)
+ if (dik != 0)
+ devinfo->keyboard.state[dik] = 0x80;
+
+ // set this flag so that we continue to use win32 until all keys are up
+ keyboard_win32_reported_key_down = TRUE;
+ }
+ }
+}
+
+
+//============================================================
+// win32_lightgun_poll
+//============================================================
+
+static void win32_lightgun_poll(device_info *devinfo)
+{
+ INT32 xpos = 0, ypos = 0;
+ POINT mousepos;
+
+ // if we are using the shared axis hack, the data is updated via Windows messages only
+ if (lightgun_shared_axis_mode)
+ return;
+
+ // get the cursor position and transform into final results
+ GetCursorPos(&mousepos);
+ if (win_window_list != NULL)
+ {
+ RECT client_rect;
+
+ // get the position relative to the window
+ GetClientRect(win_window_list->hwnd, &client_rect);
+ ScreenToClient(win_window_list->hwnd, &mousepos);
+
+ // convert to absolute coordinates
+ xpos = normalize_absolute_axis(mousepos.x, client_rect.left, client_rect.right);
+ ypos = normalize_absolute_axis(mousepos.y, client_rect.top, client_rect.bottom);
+ }
+
+ // update the X/Y positions
+ devinfo->mouse.state.lX = xpos;
+ devinfo->mouse.state.lY = ypos;
+}
+
+
+//============================================================
+// dinput_init
+//============================================================
+
+static void dinput_init(running_machine *machine)
+{
+ HRESULT result;
+
+ // first attempt to initialize DirectInput at the current version
+ dinput_version = DIRECTINPUT_VERSION;
+ result = DirectInputCreate(GetModuleHandle(NULL), dinput_version, &dinput, NULL);
+ if (result != DI_OK)
+ {
+ // if that fails, try version 5
+ dinput_version = 0x0500;
+ result = DirectInputCreate(GetModuleHandle(NULL), dinput_version, &dinput, NULL);
+ if (result != DI_OK)
+ {
+ // if that fails, try version 3
+ dinput_version = 0x0300;
+ result = DirectInputCreate(GetModuleHandle(NULL), dinput_version, &dinput, NULL);
+ if (result != DI_OK)
+ {
+ dinput_version = 0;
+ return;
+ }
+ }
+ }
+ mame_printf_verbose("DirectInput: Using DirectInput %d\n", dinput_version >> 8);
+
+ // we need an exit callback
+ add_exit_callback(machine, dinput_exit);
+
+ // initialize keyboard devices, but only if we don't have any yet
+ if (keyboard_list == NULL)
+ {
+ // enumerate the ones we have
+ result = IDirectInput_EnumDevices(dinput, DIDEVTYPE_KEYBOARD, dinput_keyboard_enum, 0, DIEDFL_ATTACHEDONLY);
+ if (result != DI_OK)
+ fatalerror("DirectInput: Unable to enumerate keyboards (result=%08X)\n", (UINT32)result);
+ }
+
+ // initialize mouse & lightgun devices, but only if we don't have any yet
+ if (mouse_list == NULL)
+ {
+ // enumerate the ones we have
+ result = IDirectInput_EnumDevices(dinput, DIDEVTYPE_MOUSE, dinput_mouse_enum, 0, DIEDFL_ATTACHEDONLY);
+ if (result != DI_OK)
+ fatalerror("DirectInput: Unable to enumerate mice (result=%08X)\n", (UINT32)result);
+ }
+
+ // initialize joystick devices
+ result = IDirectInput_EnumDevices(dinput, DIDEVTYPE_JOYSTICK, dinput_joystick_enum, 0, DIEDFL_ATTACHEDONLY);
+ if (result != DI_OK)
+ fatalerror("DirectInput: Unable to enumerate joysticks (result=%08X)\n", (UINT32)result);
+}
+
+
+//============================================================
+// dinput_exit
+//============================================================
+
+static void dinput_exit(running_machine *machine)
+{
+ // release all our devices
+ while (joystick_list != NULL && joystick_list->dinput.device != NULL)
+ dinput_device_release(joystick_list);
+ while (lightgun_list != NULL && lightgun_list->dinput.device != NULL)
+ dinput_device_release(lightgun_list);
+ while (mouse_list != NULL && mouse_list->dinput.device != NULL)
+ dinput_device_release(mouse_list);
+ while (keyboard_list != NULL && keyboard_list->dinput.device != NULL)
+ dinput_device_release(keyboard_list);
+
+ // release DirectInput
+ if (dinput != NULL)
+ IDirectInput_Release(dinput);
+ dinput = NULL;
+}
+
+
+//============================================================
+// dinput_set_dword_property
+//============================================================
+
+static HRESULT dinput_set_dword_property(LPDIRECTINPUTDEVICE device, REFGUID property_guid, DWORD object, DWORD how, DWORD value)
+{
+ DIPROPDWORD dipdw;
+
+ dipdw.diph.dwSize = sizeof(dipdw);
+ dipdw.diph.dwHeaderSize = sizeof(dipdw.diph);
+ dipdw.diph.dwObj = object;
+ dipdw.diph.dwHow = how;
+ dipdw.dwData = value;
+
+ return IDirectInputDevice_SetProperty(device, property_guid, &dipdw.diph);
+}
+
+
+//============================================================
+// dinput_device_create
+//============================================================
+
+static device_info *dinput_device_create(device_info **devlist_head_ptr, LPCDIDEVICEINSTANCE instance, LPCDIDATAFORMAT format1, LPCDIDATAFORMAT format2, DWORD cooperative_level)
+{
+ device_info *devinfo;
+ HRESULT result;
+
+ // allocate memory for the device object
+ devinfo = generic_device_alloc(devlist_head_ptr, instance->tszInstanceName);
+
+ // attempt to create a device
+ result = IDirectInput_CreateDevice(dinput, &instance->guidInstance, &devinfo->dinput.device, NULL);
+ if (result != DI_OK)
+ goto error;
+
+ // try to get a version 2 device for it
+ result = IDirectInputDevice_QueryInterface(devinfo->dinput.device, &IID_IDirectInputDevice2, (void **)&devinfo->dinput.device2);
+ if (result != DI_OK)
+ devinfo->dinput.device2 = NULL;
+
+ // get the caps
+ devinfo->dinput.caps.dwSize = STRUCTSIZE(DIDEVCAPS);
+ result = IDirectInputDevice_GetCapabilities(devinfo->dinput.device, &devinfo->dinput.caps);
+ if (result != DI_OK)
+ goto error;
+
+ // attempt to set the data format
+ devinfo->dinput.format = format1;
+ result = IDirectInputDevice_SetDataFormat(devinfo->dinput.device, devinfo->dinput.format);
+ if (result != DI_OK)
+ {
+ // use the secondary format if available
+ if (format2 != NULL)
+ {
+ devinfo->dinput.format = format2;
+ result = IDirectInputDevice_SetDataFormat(devinfo->dinput.device, devinfo->dinput.format);
+ }
+ if (result != DI_OK)
+ goto error;
+ }
+
+ // set the cooperative level
+ result = IDirectInputDevice_SetCooperativeLevel(devinfo->dinput.device, win_window_list->hwnd, cooperative_level);
+ if (result != DI_OK)
+ goto error;
+ return devinfo;
+
+error:
+ dinput_device_release(devinfo);
+ return NULL;
+}
+
+
+//============================================================
+// dinput_device_release
+//============================================================
+
+static void dinput_device_release(device_info *devinfo)
+{
+ // release the version 2 device if present
+ if (devinfo->dinput.device2 != NULL)
+ IDirectInputDevice_Release(devinfo->dinput.device2);
+ devinfo->dinput.device2 = NULL;
+
+ // release the regular device if present
+ if (devinfo->dinput.device != NULL)
+ IDirectInputDevice_Release(devinfo->dinput.device);
+ devinfo->dinput.device = NULL;
+
+ // free the item list
+ generic_device_free(devinfo);
+}
+
+
+//============================================================
+// dinput_device_item_name
+//============================================================
+
+static const char *dinput_device_item_name(device_info *devinfo, int offset, const TCHAR *defstring, const TCHAR *suffix)
+{
+ DIDEVICEOBJECTINSTANCE instance = { 0 };
+ const TCHAR *namestring = instance.tszName;
+ TCHAR *combined;
+ HRESULT result;
+ char *utf8;
+
+ // query the key name
+ instance.dwSize = STRUCTSIZE(DIDEVICEOBJECTINSTANCE);
+ result = IDirectInputDevice_GetObjectInfo(devinfo->dinput.device, &instance, offset, DIPH_BYOFFSET);
+
+ // if we got an error and have no default string, just return NULL
+ if (result != DI_OK)
+ {
+ if (defstring == NULL)
+ return NULL;
+ namestring = defstring;
+ }
+
+ // if no suffix, return as-is
+ if (suffix == NULL)
+ return utf8_from_tstring(namestring);
+
+ // otherwise, allocate space to add the suffix
+ combined = malloc_or_die(_tcslen(namestring) + 1 + _tcslen(suffix) + 1);
+ _tcscpy(combined, namestring);
+ _tcscat(combined, TEXT(" "));
+ _tcscat(combined, suffix);
+
+ // convert to UTF8, free the temporary string, and return
+ utf8 = utf8_from_tstring(combined);
+ free(combined);
+ return utf8;
+}
+
+
+//============================================================
+// dinput_device_poll
+//============================================================
+
+static HRESULT dinput_device_poll(device_info *devinfo)
+{
+ HRESULT result;
+
+ // first poll the device, then get the state
+ if (devinfo->dinput.device2 != NULL)
+ IDirectInputDevice2_Poll(devinfo->dinput.device2);
+ result = IDirectInputDevice_GetDeviceState(devinfo->dinput.device, devinfo->dinput.format->dwDataSize, &devinfo->joystick.state);
+
+ // handle lost inputs here
+ if (result == DIERR_INPUTLOST || result == DIERR_NOTACQUIRED)
+ {
+ result = IDirectInputDevice_Acquire(devinfo->dinput.device);
+ if (result == DI_OK)
+ result = IDirectInputDevice_GetDeviceState(devinfo->dinput.device, devinfo->dinput.format->dwDataSize, &devinfo->joystick.state);
+ }
+
+ return result;
+}
+
+
+//============================================================
+// dinput_keyboard_enum
+//============================================================
+
+static BOOL CALLBACK dinput_keyboard_enum(LPCDIDEVICEINSTANCE instance, LPVOID ref)
+{
+ device_info *devinfo;
+ int keynum;
+
+ // allocate and link in a new device
+ devinfo = dinput_device_create(&keyboard_list, instance, &c_dfDIKeyboard, NULL, DISCL_FOREGROUND | DISCL_NONEXCLUSIVE);
+ if (devinfo == NULL)
+ goto exit;
+
+ // add the device
+ devinfo->device = input_device_add(DEVICE_CLASS_KEYBOARD, devinfo->name, devinfo);
+ devinfo->poll = dinput_keyboard_poll;
+
+ // populate it
+ for (keynum = 0; keynum < MAX_KEYS; keynum++)
+ {
+ input_item_id itemid = keyboard_map_scancode_to_itemid(keynum);
+ TCHAR defname[20];
+ const char *name;
+
+ // generate/fetch the name
+ _sntprintf(defname, ARRAY_LENGTH(defname), TEXT("Scan%03d"), keynum);
+ name = dinput_device_item_name(devinfo, keynum, defname, NULL);
+
+ // add the item to the device
+ input_device_item_add(devinfo->device, name, &devinfo->keyboard.state[keynum], itemid, generic_button_get_state);
+ free((void *)name);
+ }
+
+exit:
+ return DIENUM_CONTINUE;
+}
+
+
+//============================================================
+// dinput_keyboard_poll
+//============================================================
+
+static void dinput_keyboard_poll(device_info *devinfo)
+{
+ HRESULT result = dinput_device_poll(devinfo);
+
+ // for the first device, if we errored, or if we previously reported win32 keys,
+ // then ignore the dinput state and poll using win32
+ if (devinfo == keyboard_list && (result != DI_OK || keyboard_win32_reported_key_down))
+ win32_keyboard_poll(devinfo);
+}
+
+
+//============================================================
+// dinput_mouse_enum
+//============================================================
+
+static BOOL CALLBACK dinput_mouse_enum(LPCDIDEVICEINSTANCE instance, LPVOID ref)
+{
+ device_info *devinfo, *guninfo = NULL;
+ int axisnum, butnum;
+ HRESULT result;
+
+ // allocate and link in a new device
+ devinfo = dinput_device_create(&mouse_list, instance, &c_dfDIMouse2, &c_dfDIMouse, DISCL_FOREGROUND | DISCL_NONEXCLUSIVE);
+ if (devinfo == NULL)
+ goto exit;
+
+ // allocate a second device for the gun (unless we are using the shared axis mode)
+ // we only support a single gun in dinput mode, so only add one
+ if (!lightgun_shared_axis_mode && devinfo == mouse_list)
+ {
+ guninfo = generic_device_alloc(&lightgun_list, instance->tszInstanceName);
+ if (guninfo == NULL)
+ goto exit;
+ }
+
+ // set relative mode on the mouse device
+ result = dinput_set_dword_property(devinfo->dinput.device, DIPROP_AXISMODE, 0, DIPH_DEVICE, DIPROPAXISMODE_REL);
+ if (result != DI_OK)
+ {
+ mame_printf_error("DirectInput: Unable to set relative mode for mouse %d (%s)\n", generic_device_index(mouse_list, devinfo), devinfo->name);
+ goto error;
+ }
+
+ // add the device
+ devinfo->device = input_device_add(DEVICE_CLASS_MOUSE, devinfo->name, devinfo);
+ devinfo->poll = dinput_mouse_poll;
+ if (guninfo != NULL)
+ {
+ guninfo->device = input_device_add(DEVICE_CLASS_LIGHTGUN, guninfo->name, guninfo);
+ guninfo->poll = win32_lightgun_poll;
+ }
+
+ // cap the number of axes and buttons based on the format
+ devinfo->dinput.caps.dwAxes = MIN(devinfo->dinput.caps.dwAxes, 3);
+ devinfo->dinput.caps.dwButtons = MIN(devinfo->dinput.caps.dwButtons, (devinfo->dinput.format == &c_dfDIMouse) ? 4 : 8);
+
+ // populate the axes
+ for (axisnum = 0; axisnum < devinfo->dinput.caps.dwAxes; axisnum++)
+ {
+ const char *name = dinput_device_item_name(devinfo, offsetof(DIMOUSESTATE, lX) + axisnum * sizeof(LONG), default_axis_name[axisnum], NULL);
+
+ // add to the mouse device and optionally to the gun device as well
+ input_device_item_add(devinfo->device, name, &devinfo->mouse.state.lX + axisnum, ITEM_ID_XAXIS + axisnum, generic_axis_get_state);
+ if (guninfo != NULL && axisnum < 2)
+ input_device_item_add(guninfo->device, name, &guninfo->mouse.state.lX + axisnum, ITEM_ID_XAXIS + axisnum, generic_axis_get_state);
+
+ free((void *)name);
+ }
+
+ // populate the buttons
+ for (butnum = 0; butnum < devinfo->dinput.caps.dwButtons; butnum++)
+ {
+ const char *name = dinput_device_item_name(devinfo, offsetof(DIMOUSESTATE, rgbButtons[butnum]), default_button_name(butnum), NULL);
+
+ // add to the mouse device and optionally to the gun device as well
+ // note that the gun device points to the mouse buttons rather than its own
+ input_device_item_add(devinfo->device, name, &devinfo->mouse.state.rgbButtons[butnum], ITEM_ID_BUTTON1 + butnum, generic_button_get_state);
+ if (guninfo != NULL)
+ input_device_item_add(guninfo->device, name, &devinfo->mouse.state.rgbButtons[butnum], ITEM_ID_BUTTON1 + butnum, generic_button_get_state);
+
+ free((void *)name);
+ }
+
+exit:
+ return DIENUM_CONTINUE;
+
+error:
+ if (guninfo != NULL)
+ generic_device_free(guninfo);
+ if (devinfo != NULL)
+ dinput_device_release(devinfo);
+ goto exit;
+}
+
+
+//============================================================
+// dinput_mouse_poll
+//============================================================
+
+static void dinput_mouse_poll(device_info *devinfo)
+{
+ // poll
+ dinput_device_poll(devinfo);
+
+ // scale the axis data
+ devinfo->mouse.state.lX *= INPUT_RELATIVE_PER_PIXEL;
+ devinfo->mouse.state.lY *= INPUT_RELATIVE_PER_PIXEL;
+ devinfo->mouse.state.lZ *= INPUT_RELATIVE_PER_PIXEL;
+}
+
+
+//============================================================
+// dinput_joystick_enum
+//============================================================
+
+static BOOL CALLBACK dinput_joystick_enum(LPCDIDEVICEINSTANCE instance, LPVOID ref)
+{
+ DWORD cooperative_level = (HAS_WINDOW_MENU ? DISCL_BACKGROUND : DISCL_FOREGROUND) | DISCL_EXCLUSIVE;
+ int axisnum, axiscount, povnum, butnum;
+ device_info *devinfo;
+ HRESULT result;
+
+ // allocate and link in a new device
+ devinfo = dinput_device_create(&joystick_list, instance, &c_dfDIJoystick, NULL, cooperative_level);
+ if (devinfo == NULL)
+ goto exit;
+
+ // set absolute mode
+ result = dinput_set_dword_property(devinfo->dinput.device, DIPROP_AXISMODE, 0, DIPH_DEVICE, DIPROPAXISMODE_ABS);
+ if (result != DI_OK)
+ mame_printf_warning("DirectInput: Unable to set absolute mode for joystick %d (%s)\n", generic_device_index(joystick_list, devinfo), devinfo->name);
+
+ // turn off deadzone; we do our own calculations
+ result = dinput_set_dword_property(devinfo->dinput.device, DIPROP_DEADZONE, 0, DIPH_DEVICE, 0);
+ if (result != DI_OK)
+ mame_printf_warning("DirectInput: Unable to reset deadzone for joystick %d (%s)\n", generic_device_index(joystick_list, devinfo), devinfo->name);
+
+ // turn off saturation; we do our own calculations
+ result = dinput_set_dword_property(devinfo->dinput.device, DIPROP_SATURATION, 0, DIPH_DEVICE, 10000);
+ if (result != DI_OK)
+ mame_printf_warning("DirectInput: Unable to reset saturation for joystick %d (%s)\n", generic_device_index(joystick_list, devinfo), devinfo->name);
+
+ // cap the number of axes, POVs, and buttons based on the format
+ devinfo->dinput.caps.dwAxes = MIN(devinfo->dinput.caps.dwAxes, 8);
+ devinfo->dinput.caps.dwPOVs = MIN(devinfo->dinput.caps.dwPOVs, 4);
+ devinfo->dinput.caps.dwButtons = MIN(devinfo->dinput.caps.dwButtons, 128);
+
+ // add the device
+ devinfo->device = input_device_add(DEVICE_CLASS_JOYSTICK, devinfo->name, devinfo);
+ devinfo->poll = dinput_joystick_poll;
+
+ // populate the axes
+ for (axisnum = axiscount = 0; axiscount < devinfo->dinput.caps.dwAxes && axisnum < 8; axisnum++)
+ {
+ DIPROPRANGE dipr;
+ const char *name;
+
+ // fetch the range of this axis
+ dipr.diph.dwSize = sizeof(dipr);
+ dipr.diph.dwHeaderSize = sizeof(dipr.diph);
+ dipr.diph.dwObj = offsetof(DIJOYSTATE2, lX) + axisnum * sizeof(LONG);
+ dipr.diph.dwHow = DIPH_BYOFFSET;
+ result = IDirectInputDevice_GetProperty(devinfo->dinput.device, DIPROP_RANGE, &dipr.diph);
+ if (result != DI_OK)
+ continue;
+ devinfo->joystick.rangemin[axisnum] = dipr.lMin;
+ devinfo->joystick.rangemax[axisnum] = dipr.lMax;
+
+ // populate the item description as well
+ name = dinput_device_item_name(devinfo, offsetof(DIJOYSTATE2, lX) + axisnum * sizeof(LONG), default_axis_name[axisnum], NULL);
+ input_device_item_add(devinfo->device, name, &devinfo->joystick.state.lX + axisnum, ITEM_ID_XAXIS + axisnum, generic_axis_get_state);
+ free((void *)name);
+
+ axiscount++;
+ }
+
+ // populate the POVs
+ for (povnum = 0; povnum < devinfo->dinput.caps.dwPOVs; povnum++)
+ {
+ const char *name;
+
+ // left
+ name = dinput_device_item_name(devinfo, offsetof(DIJOYSTATE2, rgdwPOV) + povnum * sizeof(DWORD), default_pov_name(povnum), TEXT("L"));
+ input_device_item_add(devinfo->device, name, (void *)(FPTR)(povnum * 4 + POVDIR_LEFT), ITEM_ID_OTHER_SWITCH, dinput_joystick_pov_get_state);
+ free((void *)name);
+
+ // right
+ name = dinput_device_item_name(devinfo, offsetof(DIJOYSTATE2, rgdwPOV) + povnum * sizeof(DWORD), default_pov_name(povnum), TEXT("R"));
+ input_device_item_add(devinfo->device, name, (void *)(FPTR)(povnum * 4 + POVDIR_RIGHT), ITEM_ID_OTHER_SWITCH, dinput_joystick_pov_get_state);
+ free((void *)name);
+
+ // up
+ name = dinput_device_item_name(devinfo, offsetof(DIJOYSTATE2, rgdwPOV) + povnum * sizeof(DWORD), default_pov_name(povnum), TEXT("U"));
+ input_device_item_add(devinfo->device, name, (void *)(FPTR)(povnum * 4 + POVDIR_UP), ITEM_ID_OTHER_SWITCH, dinput_joystick_pov_get_state);
+ free((void *)name);
+
+ // down
+ name = dinput_device_item_name(devinfo, offsetof(DIJOYSTATE2, rgdwPOV) + povnum * sizeof(DWORD), default_pov_name(povnum), TEXT("D"));
+ input_device_item_add(devinfo->device, name, (void *)(FPTR)(povnum * 4 + POVDIR_DOWN), ITEM_ID_OTHER_SWITCH, dinput_joystick_pov_get_state);
+ free((void *)name);
+ }
+
+ // populate the buttons
+ for (butnum = 0; butnum < devinfo->dinput.caps.dwButtons; butnum++)
+ {
+ const char *name = dinput_device_item_name(devinfo, offsetof(DIJOYSTATE2, rgbButtons[butnum]), default_button_name(butnum), NULL);
+ input_device_item_add(devinfo->device, name, &devinfo->joystick.state.rgbButtons[butnum], (butnum < 16) ? (ITEM_ID_BUTTON1 + butnum) : ITEM_ID_OTHER_SWITCH, generic_button_get_state);
+ free((void *)name);
+ }
+
+exit:
+ return DIENUM_CONTINUE;
+}
+
+
+//============================================================
+// dinput_joystick_poll
+//============================================================
+
+static void dinput_joystick_poll(device_info *devinfo)
+{
+ int axisnum;
+
+ // poll the device first
+ dinput_device_poll(devinfo);
+
+ // normalize axis values
+ for (axisnum = 0; axisnum < 8; axisnum++)
+ {
+ LONG *axis = (&devinfo->joystick.state.lX) + axisnum;
+ *axis = normalize_absolute_axis(*axis, devinfo->joystick.rangemin[axisnum], devinfo->joystick.rangemax[axisnum]);
+ }
+}
+
+
+//============================================================
+// dinput_joystick_pov_get_state
+//============================================================
+
+static INT32 dinput_joystick_pov_get_state(void *device_internal, void *item_internal)
+{
+ device_info *devinfo = device_internal;
+ int povnum = (FPTR)item_internal / 4;
+ int povdir = (FPTR)item_internal % 4;
+ INT32 result = 0;
+ DWORD pov;
+
+ // get the current state
+ poll_if_necessary();
+ pov = devinfo->joystick.state.rgdwPOV[povnum];
+
+ // if invalid, return 0
+ if ((pov & 0xffff) == 0xffff)
+ return result;
+
+ // return the current state
+ switch (povdir)
+ {
+ case POVDIR_LEFT: result = (pov >= 22500 && pov <= 31500); break;
+ case POVDIR_RIGHT: result = (pov >= 4500 && pov <= 13500); break;
+ case POVDIR_UP: result = (pov >= 31500 || pov <= 4500); break;
+ case POVDIR_DOWN: result = (pov >= 13500 && pov <= 22500); break;
+ }
+ return result;
+}
+
+
+//============================================================
+// rawinput_init
+//============================================================
+
+static void rawinput_init(running_machine *machine)
+{
+ RAWINPUTDEVICELIST *devlist = NULL;
+ int device_count, devnum, regcount;
+ RAWINPUTDEVICE reglist[2];
+ HMODULE user32;
+
+ // we need pause and exit callbacks
+ add_exit_callback(machine, rawinput_exit);
+
+ // look in user32 for the raw input APIs
+ user32 = LoadLibrary(TEXT("user32.dll"));
+ if (user32 == NULL)
+ goto error;
+
+ // look up the entry points
+ register_rawinput_devices = (register_rawinput_devices_ptr)GetProcAddress(user32, "RegisterRawInputDevices");
+ get_rawinput_device_list = (get_rawinput_device_list_ptr)GetProcAddress(user32, "GetRawInputDeviceList");
+ get_rawinput_device_info = (get_rawinput_device_info_ptr)GetProcAddress(user32, "GetRawInputDeviceInfo" UNICODE_SUFFIX);
+ get_rawinput_data = (get_rawinput_data_ptr)GetProcAddress(user32, "GetRawInputData");
+ if (register_rawinput_devices == NULL || get_rawinput_device_list == NULL || get_rawinput_device_info == NULL || get_rawinput_data == NULL)
+ goto error;
+ mame_printf_verbose("RawInput: APIs detected\n");
+
+ // get the number of devices, allocate a device list, and fetch it
+ if ((*get_rawinput_device_list)(NULL, &device_count, sizeof(*devlist)) != 0)
+ goto error;
+ if (device_count == 0)
+ goto error;
+ devlist = malloc_or_die(device_count * sizeof(*devlist));
+ if ((*get_rawinput_device_list)(devlist, &device_count, sizeof(*devlist)) == -1)
+ goto error;
+
+ // iterate backwards through devices; new devices are added at the head
+ for (devnum = device_count - 1; devnum >= 0; devnum--)
+ {
+ RAWINPUTDEVICELIST *device = &devlist[devnum];
+
+ // handle keyboards
+ if (device->dwType == RIM_TYPEKEYBOARD && !FORCE_DIRECTINPUT)
+ rawinput_keyboard_enum(device);
+
+ // handle mice
+ else if (device->dwType == RIM_TYPEMOUSE && !FORCE_DIRECTINPUT)
+ rawinput_mouse_enum(device);
+ }
+
+ // finally, register to recieve raw input WM_INPUT messages
+ regcount = 0;
+ if (keyboard_list != NULL)
+ {
+ reglist[regcount].usUsagePage = 0x01;
+ reglist[regcount].usUsage = 0x06;
+ reglist[regcount].dwFlags = RIDEV_INPUTSINK;
+ reglist[regcount].hwndTarget = win_window_list->hwnd;
+ regcount++;
+ }
+ if (mouse_list != NULL)
+ {
+ reglist[regcount].usUsagePage = 0x01;
+ reglist[regcount].usUsage = 0x02;
+ reglist[regcount].dwFlags = 0;
+ reglist[regcount].hwndTarget = win_window_list->hwnd;
+ regcount++;
+ }
+
+ // if the registration fails, we need to back off
+ if (regcount > 0)
+ if (!(*register_rawinput_devices)(reglist, regcount, sizeof(reglist[0])))
+ goto error;
+
+ free(devlist);
+ return;
+
+error:
+ if (devlist != NULL)
+ free(devlist);
+}
+
+
+//============================================================
+// rawinput_exit
+//============================================================
+
+static void rawinput_exit(running_machine *machine)
+{
+ // release all our devices
+ while (lightgun_list != NULL && lightgun_list->rawinput.device != NULL)
+ rawinput_device_release(lightgun_list);
+ while (mouse_list != NULL && mouse_list->rawinput.device != NULL)
+ rawinput_device_release(mouse_list);
+ while (keyboard_list != NULL && keyboard_list->rawinput.device != NULL)
+ rawinput_device_release(keyboard_list);
+}
+
+
+//============================================================
+// rawinput_device_create
+//============================================================
+
+static device_info *rawinput_device_create(device_info **devlist_head_ptr, PRAWINPUTDEVICELIST device)
+{
+ device_info *devinfo = NULL;
+ TCHAR *tname = NULL;
+ INT name_length;
+
+ // determine the length of the device name, allocate it, and fetch it
+ if ((*get_rawinput_device_info)(device->hDevice, RIDI_DEVICENAME, NULL, &name_length) != 0)
+ goto error;
+ tname = malloc_or_die(name_length * sizeof(*tname));
+ if ((*get_rawinput_device_info)(device->hDevice, RIDI_DEVICENAME, tname, &name_length) == -1)
+ goto error;
+
+ // if this is an RDP name, skip it
+ if (_tcsstr(tname, TEXT("Root#RDP_")) != NULL)
+ goto error;
+
+ // improve the name and then allocate a device
+ tname = rawinput_device_improve_name(tname);
+ devinfo = generic_device_alloc(devlist_head_ptr, tname);
+ free(tname);
+
+ // copy the handle
+ devinfo->rawinput.device = device->hDevice;
+ return devinfo;
+
+error:
+ if (tname != NULL)
+ free(tname);
+ if (devinfo != NULL)
+ rawinput_device_release(devinfo);
+ return NULL;
+}
+
+
+//============================================================
+// rawinput_device_release
+//============================================================
+
+static void rawinput_device_release(device_info *devinfo)
+{
+ // free the item list
+ generic_device_free(devinfo);
+}
+
+
+//============================================================
+// rawinput_device_name
+//============================================================
+
+static TCHAR *rawinput_device_improve_name(TCHAR *name)
+{
+ static const TCHAR *usbbasepath = TEXT("SYSTEM\\CurrentControlSet\\Enum\\USB");
+ static const TCHAR *basepath = TEXT("SYSTEM\\CurrentControlSet\\Enum\\");
+ TCHAR *regstring = NULL;
+ TCHAR *parentid = NULL;
+ TCHAR *regpath = NULL;
+ const TCHAR *chsrc;
+ HKEY regkey = NULL;
+ int usbindex;
+ TCHAR *chdst;
+ LONG result;
+
+ // ensure the name is something we can handle
+ if (_tcsncmp(name, TEXT("\\\\?\\"), 4) != 0)
+ return name;
+
+ // allocate a temporary string and concatenate the base path plus the name
+ regpath = malloc_or_die(sizeof(*regpath) * (_tcslen(basepath) + 1 + _tcslen(name)));
+ _tcscpy(regpath, basepath);
+ chdst = regpath + _tcslen(regpath);
+
+ // convert all # to \ in the name
+ for (chsrc = name + 4; *chsrc != 0; chsrc++)
+ *chdst++ = (*chsrc == '#') ? '\\' : *chsrc;
+ *chdst = 0;
+
+ // remove the final chunk
+ chdst = _tcsrchr(regpath, '\\');
+ if (chdst == NULL)
+ goto exit;
+ *chdst = 0;
+
+ // now try to open the registry key
+ result = RegOpenKeyEx(HKEY_LOCAL_MACHINE, regpath, 0, KEY_READ, &regkey);
+ if (result != ERROR_SUCCESS)
+ goto exit;
+
+ // fetch the device description; if it exists, we are finished
+ regstring = reg_query_string(regkey, TEXT("DeviceDesc"));
+ if (regstring != NULL)
+ goto convert;
+
+ // close this key
+ RegCloseKey(regkey);
+ regkey = NULL;
+
+ // if the key name does not contain "HID", it's not going to be in the USB tree; give up
+ if (_tcsstr(regpath, TEXT("HID")) == NULL)
+ goto exit;
+
+ // extract the expected parent ID from the regpath
+ parentid = _tcsrchr(regpath, '\\');
+ if (parentid == NULL)
+ goto exit;
+ parentid++;
+
+ // open the USB key
+ result = RegOpenKeyEx(HKEY_LOCAL_MACHINE, usbbasepath, 0, KEY_READ, &regkey);
+ if (result != ERROR_SUCCESS)
+ goto exit;
+
+ // enumerate the USB key
+ for (usbindex = 0; result == ERROR_SUCCESS && regstring == NULL; usbindex++)
+ {
+ TCHAR keyname[MAX_PATH];
+ DWORD namelen;
+
+ // get the next enumerated subkey and scan it
+ namelen = ARRAY_LENGTH(keyname) - 1;
+ result = RegEnumKeyEx(regkey, usbindex, keyname, &namelen, NULL, NULL, NULL, NULL);
+ if (result == ERROR_SUCCESS)
+ {
+ LONG subresult;
+ int subindex;
+ HKEY subkey;
+
+ // open the subkey
+ subresult = RegOpenKeyEx(regkey, keyname, 0, KEY_READ, &subkey);
+ if (subresult != ERROR_SUCCESS)
+ continue;
+
+ // enumerate the subkey
+ for (subindex = 0; subresult == ERROR_SUCCESS && regstring == NULL; subindex++)
+ {
+ // get the next enumerated subkey and scan it
+ namelen = ARRAY_LENGTH(keyname) - 1;
+ subresult = RegEnumKeyEx(subkey, subindex, keyname, &namelen, NULL, NULL, NULL, NULL);
+ if (subresult == ERROR_SUCCESS)
+ {
+ TCHAR *endparentid;
+ LONG endresult;
+ HKEY endkey;
+
+ // open this final key
+ endresult = RegOpenKeyEx(subkey, keyname, 0, KEY_READ, &endkey);
+ if (endresult != ERROR_SUCCESS)
+ continue;
+
+ // do we have a match?
+ endparentid = reg_query_string(endkey, TEXT("ParentIdPrefix"));
+ if (endparentid != NULL && _tcsncmp(parentid, endparentid, _tcslen(endparentid)) == 0)
+ regstring = reg_query_string(endkey, TEXT("DeviceDesc"));
+
+ // free memory and close the key
+ if (endparentid != NULL)
+ free(endparentid);
+ RegCloseKey(endkey);
+ }
+ }
+
+ // close the subkey
+ RegCloseKey(subkey);
+ }
+ }
+
+ // if we didn't find anything, go to the exit
+ if (regstring == NULL)
+ goto exit;
+
+convert:
+ // replace the name with the nicer one
+ free(name);
+
+ // remove anything prior to the final semicolon
+ chsrc = _tcsrchr(regstring, ';');
+ if (chsrc != NULL)
+ chsrc++;
+ else
+ chsrc = regstring;
+ name = malloc_or_die(sizeof(*name) * (_tcslen(chsrc) + 1));
+ _tcscpy(name, chsrc);
+
+exit:
+ if (regstring != NULL)
+ free(regstring);
+ if (regpath != NULL)
+ free(regpath);
+ if (regkey != NULL)
+ RegCloseKey(regkey);
+
+ return name;
+}
+
+
+//============================================================
+// rawinput_keyboard_enum
+//============================================================
+
+static void rawinput_keyboard_enum(PRAWINPUTDEVICELIST device)
+{
+ device_info *devinfo;
+ int keynum;
+
+ // allocate and link in a new device
+ devinfo = rawinput_device_create(&keyboard_list, device);
+ if (devinfo == NULL)
+ return;
+
+ // add the device
+ devinfo->device = input_device_add(DEVICE_CLASS_KEYBOARD, devinfo->name, devinfo);
+
+ // populate it
+ for (keynum = 0; keynum < MAX_KEYS; keynum++)
+ {
+ input_item_id itemid = keyboard_map_scancode_to_itemid(keynum);
+ TCHAR keyname[100];
+ const char *name;
+
+ // generate the name
+ if (GetKeyNameText(((keynum & 0x7f) << 16) | ((keynum & 0x80) << 17), keyname, ARRAY_LENGTH(keyname)) == 0)
+ _sntprintf(keyname, ARRAY_LENGTH(keyname), TEXT("Scan%03d"), keynum);
+ name = utf8_from_tstring(keyname);
+
+ // add the item to the device
+ input_device_item_add(devinfo->device, name, &devinfo->keyboard.state[keynum], itemid, generic_button_get_state);
+ free((void *)name);
+ }
+}
+
+
+//============================================================
+// rawinput_keyboard_update
+//============================================================
+
+static void rawinput_keyboard_update(HANDLE device, RAWKEYBOARD *data)
+{
+ device_info *devinfo;
+
+ // find the keyboard in the list and process
+ for (devinfo = keyboard_list; devinfo != NULL; devinfo = devinfo->next)
+ if (devinfo->rawinput.device == device)
+ {
+ // determine the full DIK-compatible scancode
+ UINT8 scancode = (data->MakeCode & 0x7f) | ((data->Flags & RI_KEY_E0) ? 0x80 : 0x00);
+
+ // scancode 0xaa is a special shift code we need to ignore
+ if (scancode == 0xaa)
+ break;
+
+ // set or clear the key
+ if (!(data->Flags & RI_KEY_BREAK))
+ devinfo->keyboard.state[scancode] = 0x80;
+ else
+ devinfo->keyboard.state[scancode] = 0x00;
+ break;
+ }
+}
+
+
+//============================================================
+// rawinput_mouse_enum
+//============================================================
+
+static void rawinput_mouse_enum(PRAWINPUTDEVICELIST device)
+{
+ device_info *devinfo, *guninfo = NULL;
+ int axisnum, butnum;
+
+ // allocate and link in a new mouse device
+ devinfo = rawinput_device_create(&mouse_list, device);
+ if (devinfo == NULL)
+ return;
+ devinfo->poll = rawinput_mouse_poll;
+
+ // allocate a second device for the gun (unless we are using the shared axis mode)
+ if (!lightgun_shared_axis_mode)
+ {
+ guninfo = rawinput_device_create(&lightgun_list, device);
+ assert(guninfo != NULL);
+ }
+
+ // add the device
+ devinfo->device = input_device_add(DEVICE_CLASS_MOUSE, devinfo->name, devinfo);
+ if (guninfo != NULL)
+ {
+ guninfo->device = input_device_add(DEVICE_CLASS_LIGHTGUN, guninfo->name, guninfo);
+ guninfo->poll = (guninfo == lightgun_list) ? win32_lightgun_poll : NULL;
+ guninfo->mouse.partner = &devinfo->mouse;
+ }
+
+ // populate the axes
+ for (axisnum = 0; axisnum < 3; axisnum++)
+ {
+ const char *name = utf8_from_tstring(default_axis_name[axisnum]);
+
+ // add to the mouse device and optionally to the gun device as well
+ input_device_item_add(devinfo->device, name, &devinfo->mouse.state.lX + axisnum, ITEM_ID_XAXIS + axisnum, generic_axis_get_state);
+ if (guninfo != NULL && axisnum < 2)
+ input_device_item_add(guninfo->device, name, &guninfo->mouse.state.lX + axisnum, ITEM_ID_XAXIS + axisnum, generic_axis_get_state);
+
+ free((void *)name);
+ }
+
+ // populate the buttons
+ for (butnum = 0; butnum < 5; butnum++)
+ {
+ const char *name = utf8_from_tstring(default_button_name(butnum));
+
+ // add to the mouse device and optionally to the gun device as well
+ // note that the gun device points to the mouse buttons rather than its own
+ input_device_item_add(devinfo->device, name, &devinfo->mouse.state.rgbButtons[butnum], ITEM_ID_BUTTON1 + butnum, generic_button_get_state);
+ if (guninfo != NULL)
+ input_device_item_add(guninfo->device, name, &devinfo->mouse.state.rgbButtons[butnum], ITEM_ID_BUTTON1 + butnum, generic_button_get_state);
+
+ free((void *)name);
+ }
+}
+
+
+//============================================================
+// rawinput_mouse_update
+//============================================================
+
+static void rawinput_mouse_update(HANDLE device, RAWMOUSE *data)
+{
+ device_info *devlist = (data->usFlags & MOUSE_MOVE_ABSOLUTE) ? lightgun_list : mouse_list;
+ device_info *devinfo;
+
+ // find the mouse in the list and process
+ for (devinfo = devlist; devinfo != NULL; devinfo = devinfo->next)
+ if (devinfo->rawinput.device == device)
+ {
+ mouse_state *mouseinfo = (devinfo->mouse.partner != NULL) ? devinfo->mouse.partner : &devinfo->mouse;
+
+ // if we got relative data, update it as a mouse
+ if (!(data->usFlags & MOUSE_MOVE_ABSOLUTE))
+ {
+ devinfo->mouse.raw_x += data->lLastX * INPUT_RELATIVE_PER_PIXEL;
+ devinfo->mouse.raw_y += data->lLastY * INPUT_RELATIVE_PER_PIXEL;
+
+ // update zaxis
+ if (data->usButtonFlags & RI_MOUSE_WHEEL)
+ devinfo->mouse.raw_z += (INT16)data->usButtonData * INPUT_RELATIVE_PER_PIXEL;
+ }
+
+ // otherwise, update it as a lightgun
+ else
+ {
+ devinfo->mouse.raw_x = normalize_absolute_axis(data->lLastX, 0, 0xffff);
+ devinfo->mouse.raw_y = normalize_absolute_axis(data->lLastY, 0, 0xffff);
+
+ // also clear the polling function so win32_lightgun_poll isn't called
+ devinfo->poll = NULL;
+ }
+
+ // update the button states; always update the corresponding mouse buttons
+ if (data->usButtonFlags & RI_MOUSE_BUTTON_1_DOWN) mouseinfo->state.rgbButtons[0] = 0x80;
+ if (data->usButtonFlags & RI_MOUSE_BUTTON_1_UP) mouseinfo->state.rgbButtons[0] = 0x00;
+ if (data->usButtonFlags & RI_MOUSE_BUTTON_2_DOWN) mouseinfo->state.rgbButtons[1] = 0x80;
+ if (data->usButtonFlags & RI_MOUSE_BUTTON_2_UP) mouseinfo->state.rgbButtons[1] = 0x00;
+ if (data->usButtonFlags & RI_MOUSE_BUTTON_3_DOWN) mouseinfo->state.rgbButtons[2] = 0x80;
+ if (data->usButtonFlags & RI_MOUSE_BUTTON_3_UP) mouseinfo->state.rgbButtons[2] = 0x00;
+ if (data->usButtonFlags & RI_MOUSE_BUTTON_4_DOWN) mouseinfo->state.rgbButtons[3] = 0x80;
+ if (data->usButtonFlags & RI_MOUSE_BUTTON_4_UP) mouseinfo->state.rgbButtons[3] = 0x00;
+ if (data->usButtonFlags & RI_MOUSE_BUTTON_5_DOWN) mouseinfo->state.rgbButtons[4] = 0x80;
+ if (data->usButtonFlags & RI_MOUSE_BUTTON_5_UP) mouseinfo->state.rgbButtons[4] = 0x00;
+ break;
+ }
+}
+
+
+//============================================================
+// rawinput_mouse_poll
+//============================================================
+
+static void rawinput_mouse_poll(device_info *devinfo)
+{
+ poll_if_necessary();
+
+ // copy the accumulated raw state to the actual state
+ osd_lock_acquire(input_lock);
+ devinfo->mouse.state.lX = devinfo->mouse.raw_x;
+ devinfo->mouse.state.lY = devinfo->mouse.raw_y;
+ devinfo->mouse.state.lZ = devinfo->mouse.raw_z;
+ devinfo->mouse.raw_x = 0;
+ devinfo->mouse.raw_y = 0;
+ devinfo->mouse.raw_z = 0;
+ osd_lock_release(input_lock);
+}
+
+
+//============================================================
+// reg_query_string
+//============================================================
+
+static TCHAR *reg_query_string(HKEY key, const TCHAR *path)
+{
+ TCHAR *buffer;
+ DWORD datalen;
+ LONG result;
+
+ // first query to get the length
+ result = RegQueryValueEx(key, path, NULL, NULL, NULL, &datalen);
+ if (result != ERROR_SUCCESS)
+ return NULL;
+
+ // allocate a buffer
+ buffer = malloc_or_die(datalen + sizeof(*buffer));
+ buffer[datalen / sizeof(*buffer)] = 0;
+
+ // now get the actual data
+ result = RegQueryValueEx(key, path, NULL, NULL, (LPBYTE)buffer, &datalen);
+ if (result == ERROR_SUCCESS)
+ return buffer;
+
+ // otherwise return a NULL buffer
+ free(buffer);
+ return NULL;
+}
+
+
+//============================================================
+// default_button_name
+//============================================================
+
+static const TCHAR *default_button_name(int which)
+{
+ static TCHAR buffer[20];
+ _sntprintf(buffer, ARRAY_LENGTH(buffer), TEXT("B%d"), which);
+ return buffer;
+}
+
+
+//============================================================
+// default_pov_name
+//============================================================
+
+static const TCHAR *default_pov_name(int which)
+{
+ static TCHAR buffer[20];
+ _sntprintf(buffer, ARRAY_LENGTH(buffer), TEXT("POV%d"), which);
+ return buffer;
+}
diff --git a/src/osd/windows/input.h b/src/osd/windows/input.h
new file mode 100644
index 00000000000..a05c8ca05a8
--- /dev/null
+++ b/src/osd/windows/input.h
@@ -0,0 +1,27 @@
+//============================================================
+//
+// input.h - Win32 implementation of MAME input routines
+//
+// Copyright (c) 1996-2007, Nicola Salmoria and the MAME Team.
+// Visit http://mamedev.org for licensing and usage restrictions.
+//
+//============================================================
+
+#ifndef __INPUT_H
+#define __INPUT_H
+
+
+//============================================================
+// PROTOTYPES
+//============================================================
+
+void wininput_init(running_machine *machine);
+void wininput_poll(void);
+
+void wininput_handle_mouse_button(int button, int down, int x, int y);
+BOOL wininput_handle_raw(HANDLE device);
+
+int wininput_should_hide_mouse(void);
+
+
+#endif /* __INPUT_H */
diff --git a/src/osd/windows/ledutil.c b/src/osd/windows/ledutil.c
new file mode 100644
index 00000000000..c8caaaa01ae
--- /dev/null
+++ b/src/osd/windows/ledutil.c
@@ -0,0 +1,738 @@
+//============================================================
+//
+// ledutil.c - Win32 example code that tracks changing
+// outputs and updates the keyboard LEDs in response
+//
+// Copyright (c) 1996-2007, Nicola Salmoria and the MAME Team.
+// Visit http://mamedev.org for licensing and usage restrictions.
+//
+//============================================================
+//
+// This is sample code. To use it as a starting point, you
+// should do the following:
+//
+// 1. Change the CLIENT_ID define to something unique.
+//
+// 2. Change the WINDOW_CLASS and WINDOW_NAME defines to
+// something unique.
+//
+// 3. Delete all the code from the >8 snip 8< comment and
+// downward.
+//
+// 4. Implement the following functions:
+//
+// output_startup - called at app init time
+// output_shutdown - called before the app exits
+// output_mame_start - called when MAME starts
+// output_mame_stop - called when MAME exits
+// output_set_state - called whenever state changes
+//
+//============================================================
+
+// Needed for RAW Input
+#define _WIN32_WINNT 0x501
+
+// standard windows headers
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#include <winioctl.h>
+
+// standard C headers
+#include <conio.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+// MAME output header file
+typedef void running_machine;
+#include "output.h"
+
+
+
+//============================================================
+// DEBUGGING
+//============================================================
+
+// note you need to compile as a console app to have any of
+// these printfs show up
+#define DEBUG_VERSION 0
+
+#if DEBUG_VERSION
+#define DEBUG_PRINTF(x) printf x
+#else
+#define DEBUG_PRINTF(x)
+#endif
+
+
+//============================================================
+// CONSTANTS
+//============================================================
+
+// unique client ID
+#define CLIENT_ID (('M' << 24) | ('L' << 16) | ('E' << 8) | ('D' << 0))
+
+// LED methods
+#define LED_METHOD_PS2 0
+#define LED_METHOD_USB 1
+#define LED_METHOD_WIN9X 2
+
+// window parameters
+#define WINDOW_CLASS TEXT("LEDSample")
+#define WINDOW_NAME TEXT("LEDSample")
+
+// window styles
+#define WINDOW_STYLE WS_OVERLAPPEDWINDOW
+#define WINDOW_STYLE_EX 0
+
+// Define the keyboard indicators.
+// (Definitions borrowed from ntddkbd.h)
+
+#define IOCTL_KEYBOARD_SET_INDICATORS CTL_CODE(FILE_DEVICE_KEYBOARD, 0x0002, METHOD_BUFFERED, FILE_ANY_ACCESS)
+#define IOCTL_KEYBOARD_QUERY_TYPEMATIC CTL_CODE(FILE_DEVICE_KEYBOARD, 0x0008, METHOD_BUFFERED, FILE_ANY_ACCESS)
+#define IOCTL_KEYBOARD_QUERY_INDICATORS CTL_CODE(FILE_DEVICE_KEYBOARD, 0x0010, METHOD_BUFFERED, FILE_ANY_ACCESS)
+
+#define KEYBOARD_SCROLL_LOCK_ON 1
+#define KEYBOARD_NUM_LOCK_ON 2
+#define KEYBOARD_CAPS_LOCK_ON 4
+
+
+
+//============================================================
+// TYPE DEFINITIONS
+//============================================================
+
+typedef struct _KEYBOARD_INDICATOR_PARAMETERS
+{
+ USHORT UnitId; // Unit identifier.
+ USHORT LedFlags; // LED indicator state.
+} KEYBOARD_INDICATOR_PARAMETERS, *PKEYBOARD_INDICATOR_PARAMETERS;
+
+
+typedef struct _id_map_entry id_map_entry;
+struct _id_map_entry
+{
+ id_map_entry * next;
+ const char * name;
+ WPARAM id;
+};
+
+
+
+//============================================================
+// GLOBAL VARIABLES
+//============================================================
+
+static int ledmethod;
+static int original_state;
+static int current_state;
+static int pause_state;
+static HANDLE hKbdDev;
+
+static HWND mame_target;
+static HWND listener_hwnd;
+
+static id_map_entry * idmaplist;
+
+// message IDs
+static UINT om_mame_start;
+static UINT om_mame_stop;
+static UINT om_mame_update_state;
+static UINT om_mame_register_client;
+static UINT om_mame_unregister_client;
+static UINT om_mame_get_id_string;
+
+
+
+//============================================================
+// FUNCTION PROTOTYPES
+//============================================================
+
+static int create_window_class(void);
+static LRESULT CALLBACK listener_window_proc(HWND wnd, UINT message, WPARAM wparam, LPARAM lparam);
+static LRESULT handle_mame_start(WPARAM wparam, LPARAM lparam);
+static LRESULT handle_mame_stop(WPARAM wparam, LPARAM lparam);
+static LRESULT handle_copydata(WPARAM wparam, LPARAM lparam);
+static void reset_id_to_outname_cache(void);
+static const char *map_id_to_outname(WPARAM id);
+static LRESULT handle_update_state(WPARAM wparam, LPARAM lparam);
+
+// these functions provide the meat
+static void output_startup(const char *commandline);
+static void output_mame_start(void);
+static void output_set_state(const char *name, INT32 state);
+static void output_mame_stop(void);
+static void output_shutdown(void);
+
+static int led_get_state(void);
+static void led_set_state(int state);
+
+
+//============================================================
+// main
+//============================================================
+
+int utf8_main(int argc, char *argv[])
+{
+ const char *arg = (argc > 1) ? argv[1] : "";
+ int exitcode = 1;
+ HWND otherwnd;
+ MSG message;
+ int result;
+
+ // see if there is another instance of us running
+ otherwnd = FindWindow(WINDOW_CLASS, WINDOW_NAME);
+
+ // if the argument is "-kill", post a close message
+ if (strcmp(arg, "-kill") == 0)
+ {
+ if (otherwnd != NULL)
+ PostMessage(otherwnd, WM_QUIT, 0, 0);
+ return (otherwnd != NULL) ? 1 : 0;
+ }
+
+ // if we had another instance, defer to it
+ if (otherwnd != NULL)
+ return 0;
+
+ // call the startup code
+ output_startup(arg);
+
+ // create our window class
+ result = create_window_class();
+ if (result != 0)
+ goto error;
+
+ // create a window
+ listener_hwnd = CreateWindowEx(
+ WINDOW_STYLE_EX,
+ WINDOW_CLASS,
+ WINDOW_NAME,
+ WINDOW_STYLE,
+ 0, 0,
+ 1, 1,
+ NULL,
+ NULL,
+ GetModuleHandle(NULL),
+ NULL);
+ if (listener_hwnd == NULL)
+ goto error;
+
+ // allocate message ids
+ om_mame_start = RegisterWindowMessage(OM_MAME_START);
+ if (om_mame_start == 0)
+ goto error;
+ om_mame_stop = RegisterWindowMessage(OM_MAME_STOP);
+ if (om_mame_stop == 0)
+ goto error;
+ om_mame_update_state = RegisterWindowMessage(OM_MAME_UPDATE_STATE);
+ if (om_mame_update_state == 0)
+ goto error;
+
+ om_mame_register_client = RegisterWindowMessage(OM_MAME_REGISTER_CLIENT);
+ if (om_mame_register_client == 0)
+ goto error;
+ om_mame_unregister_client = RegisterWindowMessage(OM_MAME_UNREGISTER_CLIENT);
+ if (om_mame_unregister_client == 0)
+ goto error;
+ om_mame_get_id_string = RegisterWindowMessage(OM_MAME_GET_ID_STRING);
+ if (om_mame_get_id_string == 0)
+ goto error;
+
+ // see if MAME is already running
+ otherwnd = FindWindow(OUTPUT_WINDOW_CLASS, OUTPUT_WINDOW_NAME);
+ if (otherwnd != NULL)
+ handle_mame_start((WPARAM)otherwnd, 0);
+
+ // process messages
+ while (GetMessage(&message, NULL, 0, 0))
+ {
+ TranslateMessage(&message);
+ DispatchMessage(&message);
+ }
+
+ // reset on the way out if still live
+ if (mame_target != NULL)
+ handle_mame_stop((WPARAM)mame_target, 0);
+ exitcode = 0;
+
+error:
+ // call the shutdown code
+ output_shutdown();
+
+ return exitcode;
+}
+
+
+//============================================================
+// create_window_class
+//============================================================
+
+static int create_window_class(void)
+{
+ static int classes_created = FALSE;
+
+ /* only do this once */
+ if (!classes_created)
+ {
+ WNDCLASS wc = { 0 };
+
+ // initialize the description of the window class
+ wc.lpszClassName = WINDOW_CLASS;
+ wc.hInstance = GetModuleHandle(NULL);
+ wc.lpfnWndProc = listener_window_proc;
+
+ // register the class; fail if we can't
+ if (!RegisterClass(&wc))
+ return 1;
+ classes_created = TRUE;
+ }
+
+ return 0;
+}
+
+
+//============================================================
+// window_proc
+//============================================================
+
+static LRESULT CALLBACK listener_window_proc(HWND wnd, UINT message, WPARAM wparam, LPARAM lparam)
+{
+ // OM_MAME_START: register ourselves with the new MAME (first instance only)
+ if (message == om_mame_start)
+ return handle_mame_start(wparam, lparam);
+
+ // OM_MAME_STOP: no need to unregister, just note that we've stopped caring and reset the LEDs
+ else if (message == om_mame_stop)
+ return handle_mame_stop(wparam, lparam);
+
+ // OM_MAME_UPDATE_STATE: update the state of this item if we care
+ else if (message == om_mame_update_state)
+ return handle_update_state(wparam, lparam);
+
+ // WM_COPYDATA: extract the string and create an ID map entry
+ else if (message == WM_COPYDATA)
+ return handle_copydata(wparam, lparam);
+
+ // everything else is default
+ else
+ return DefWindowProc(wnd, message, wparam, lparam);
+}
+
+
+//============================================================
+// handle_mame_start
+//============================================================
+
+static LRESULT handle_mame_start(WPARAM wparam, LPARAM lparam)
+{
+ DEBUG_PRINTF(("mame_start (%08X)\n", (UINT32)wparam));
+
+ // make this the targeted version of MAME
+ mame_target = (HWND)wparam;
+
+ // initialize the LED states
+ output_mame_start();
+ reset_id_to_outname_cache();
+
+ // register ourselves as a client
+ PostMessage(mame_target, om_mame_register_client, (WPARAM)listener_hwnd, CLIENT_ID);
+
+ // get the game name
+ map_id_to_outname(0);
+ return 0;
+}
+
+
+//============================================================
+// handle_mame_stop
+//============================================================
+
+static LRESULT handle_mame_stop(WPARAM wparam, LPARAM lparam)
+{
+ DEBUG_PRINTF(("mame_stop (%08X)\n", (UINT32)wparam));
+
+ // ignore if this is not the instance we care about
+ if (mame_target != (HWND)wparam)
+ return 1;
+
+ // clear our target out
+ mame_target = NULL;
+ reset_id_to_outname_cache();
+
+ // reset the LED states
+ output_mame_stop();
+ return 0;
+}
+
+
+//============================================================
+// handle_copydata
+//============================================================
+
+static LRESULT handle_copydata(WPARAM wparam, LPARAM lparam)
+{
+ COPYDATASTRUCT *copydata = (COPYDATASTRUCT *)lparam;
+ copydata_id_string *data = (copydata_id_string *)copydata->lpData;
+ id_map_entry *entry;
+ char *string;
+
+ DEBUG_PRINTF(("copydata (%08X)\n", (UINT32)wparam));
+
+ // ignore requests we don't care about
+ if (mame_target != (HWND)wparam)
+ return 1;
+
+ // allocate memory
+ entry = malloc(sizeof(*entry));
+ string = malloc(strlen(data->string) + 1);
+
+ // if all allocations worked, make a new entry
+ if (entry != NULL && string != NULL)
+ {
+ entry->next = idmaplist;
+ entry->name = string;
+ entry->id = data->id;
+
+ // copy the string and hook us into the list
+ strcpy(string, data->string);
+ idmaplist = entry;
+
+ DEBUG_PRINTF((" id %d = '%s'\n", entry->id, entry->name));
+ }
+
+ return 0;
+}
+
+
+//============================================================
+// reset_id_to_outname_cache
+//============================================================
+
+static void reset_id_to_outname_cache(void)
+{
+ // free our ID list
+ while (idmaplist != NULL)
+ {
+ id_map_entry *temp = idmaplist;
+ idmaplist = temp->next;
+ free(temp);
+ }
+}
+
+
+//============================================================
+// map_id_to_outname
+//============================================================
+
+static const char *map_id_to_outname(WPARAM id)
+{
+ id_map_entry *entry;
+
+ // see if we have an entry in our map
+ for (entry = idmaplist; entry != NULL; entry = entry->next)
+ if (entry->id == id)
+ return entry->name;
+
+ // no entry yet; we have to ask
+ SendMessage(mame_target, om_mame_get_id_string, (WPARAM)listener_hwnd, id);
+
+ // now see if we have the entry in our map
+ for (entry = idmaplist; entry != NULL; entry = entry->next)
+ if (entry->id == id)
+ return entry->name;
+
+ // if not, use an empty string
+ return "";
+}
+
+
+//============================================================
+// handle_update_state
+//============================================================
+
+static LRESULT handle_update_state(WPARAM wparam, LPARAM lparam)
+{
+ DEBUG_PRINTF(("update_state: id=%d state=%d\n", (UINT32)wparam, (UINT32)lparam));
+ output_set_state(map_id_to_outname(wparam), lparam);
+ return 0;
+}
+
+
+//
+// END BOILERPLATE CODE
+//
+// ------------------------>8 snip 8<-------------------------
+//
+// BEGIN LED-SPECIFIC CODE
+//
+
+
+//============================================================
+// output_startup
+//============================================================
+
+static void output_startup(const char *commandline)
+{
+ OSVERSIONINFO osinfo = { sizeof(OSVERSIONINFO) };
+
+ // default to PS/2, override if USB is specified as a parameter
+ ledmethod = LED_METHOD_PS2;
+ if (commandline != NULL && strcmp(commandline, "-usb") == 0)
+ ledmethod = LED_METHOD_USB;
+
+ // force Win9x method if we're on Win 9x
+ GetVersionEx(&osinfo);
+ if (osinfo.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS)
+ ledmethod = LED_METHOD_WIN9X;
+
+ // output the method
+ switch (ledmethod)
+ {
+ case LED_METHOD_PS2:
+ DEBUG_PRINTF(("Using PS/2 method\n"));
+ break;
+
+ case LED_METHOD_USB:
+ DEBUG_PRINTF(("Using USB method\n"));
+ break;
+
+ case LED_METHOD_WIN9X:
+ DEBUG_PRINTF(("Using Win9x method\n"));
+ break;
+ }
+}
+
+
+//============================================================
+// output_shutdown
+//============================================================
+
+static void output_shutdown(void)
+{
+ // nothing to do here
+}
+
+
+//============================================================
+// output_mame_start
+//============================================================
+
+static void output_mame_start(void)
+{
+ HRESULT error_number;
+
+ // initialize the system based on the method
+ switch (ledmethod)
+ {
+ case LED_METHOD_PS2:
+ if (!DefineDosDevice(DDD_RAW_TARGET_PATH, TEXT("Kbd"), TEXT("\\Device\\KeyboardClass0")))
+ {
+ error_number = GetLastError();
+ fprintf(stderr, "Unable to open the keyboard device. (error %d)\n", (UINT32)error_number);
+ return;
+ }
+
+ hKbdDev = CreateFile(TEXT("\\\\.\\Kbd"), GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
+ if (hKbdDev == INVALID_HANDLE_VALUE)
+ {
+ error_number = GetLastError();
+ fprintf(stderr, "Unable to open the keyboard device. (error %d)\n", (UINT32)error_number);
+ return;
+ }
+ break;
+ }
+
+ // remember the initial LED states
+ original_state = current_state = led_get_state();
+}
+
+
+//============================================================
+// output_mame_stop
+//============================================================
+
+static void output_mame_stop(void)
+{
+ int error_number = 0;
+
+ // restore the initial LED states
+ led_set_state(original_state);
+
+ switch (ledmethod)
+ {
+ case LED_METHOD_PS2:
+ if (!DefineDosDevice(DDD_REMOVE_DEFINITION, TEXT("Kbd"), NULL))
+ {
+ error_number = GetLastError();
+ fprintf(stderr, "Unable to close the keyboard device. (error %d)\n", error_number);
+ return;
+ }
+ if (!CloseHandle(hKbdDev))
+ {
+ error_number = GetLastError();
+ fprintf(stderr, "Unable to close the keyboard device. (error %d)\n", error_number);
+ return;
+ }
+ break;
+ }
+}
+
+
+//============================================================
+// output_set_state
+//============================================================
+
+static void output_set_state(const char *outname, INT32 state)
+{
+ // look for pause state
+ if (strcmp(outname, "pause") == 0)
+ {
+ if (state)
+ {
+ pause_state = led_get_state();
+ led_set_state(original_state);
+ }
+ else
+ {
+ original_state = led_get_state();
+ led_set_state(pause_state);
+ }
+ }
+ // look for LED0/LED1/LED2 states and update accordingly
+ else if (strcmp(outname, "led0") == 0)
+ led_set_state((current_state & ~1) | (state & 1));
+ else if (strcmp(outname, "led1") == 0)
+ led_set_state((current_state & ~2) | ((state & 1) << 1));
+ else if (strcmp(outname, "led2") == 0)
+ led_set_state((current_state & ~4) | ((state & 1) << 2));
+}
+
+
+//============================================================
+// led_get_state
+//============================================================
+
+static int led_get_state(void)
+{
+ int result = 0;
+
+ switch (ledmethod)
+ {
+ case LED_METHOD_WIN9X:
+ case LED_METHOD_USB:
+ {
+ BYTE key_states[256];
+
+ // get the current state
+ GetKeyboardState(&key_states[0]);
+
+ // set the numlock bit
+ result |= (key_states[VK_NUMLOCK] & 1);
+ result |= (key_states[VK_CAPITAL] & 1) << 1;
+ result |= (key_states[VK_SCROLL] & 1) << 2;
+ break;
+ }
+
+ case LED_METHOD_PS2:
+ {
+ KEYBOARD_INDICATOR_PARAMETERS OutputBuffer; // Output buffer for DeviceIoControl
+ ULONG DataLength = sizeof(KEYBOARD_INDICATOR_PARAMETERS);
+ ULONG ReturnedLength; // Number of bytes returned in output buffer
+
+ // Address first keyboard
+ OutputBuffer.UnitId = 0;
+
+ DeviceIoControl(hKbdDev, IOCTL_KEYBOARD_QUERY_INDICATORS,
+ NULL, 0,
+ &OutputBuffer, DataLength,
+ &ReturnedLength, NULL);
+
+ // Demangle lights to match 95/98
+ if (OutputBuffer.LedFlags & KEYBOARD_NUM_LOCK_ON) result |= 0x1;
+ if (OutputBuffer.LedFlags & KEYBOARD_CAPS_LOCK_ON) result |= 0x2;
+ if (OutputBuffer.LedFlags & KEYBOARD_SCROLL_LOCK_ON) result |= 0x4;
+ break;
+ }
+ }
+
+ return result;
+}
+
+
+//============================================================
+// led_set_state
+//============================================================
+
+static void led_set_state(int state)
+{
+ current_state = state;
+
+ switch (ledmethod)
+ {
+ case LED_METHOD_WIN9X:
+ {
+ // thanks to Lee Taylor for the original version of this code
+ BYTE key_states[256];
+
+ // get the current state
+ GetKeyboardState(&key_states[0]);
+
+ // mask states and set new states
+ key_states[VK_NUMLOCK] = (key_states[VK_NUMLOCK] & ~1) | ((state >> 0) & 1);
+ key_states[VK_CAPITAL] = (key_states[VK_CAPITAL] & ~1) | ((state >> 1) & 1);
+ key_states[VK_SCROLL] = (key_states[VK_SCROLL] & ~1) | ((state >> 2) & 1);
+
+ SetKeyboardState(&key_states[0]);
+ break;
+ }
+
+ case LED_METHOD_USB:
+ {
+ static const BYTE vk[3] = { VK_NUMLOCK, VK_CAPITAL, VK_SCROLL };
+ BYTE keyState[256];
+ int k;
+
+ GetKeyboardState((LPBYTE)&keyState);
+ for (k = 0; k < 3; k++)
+ {
+ if ((((state >> k) & 1) && !(keyState[vk[k]] & 1)) ||
+ (!((state >> k) & 1) && (keyState[vk[k]] & 1)))
+ {
+ // Simulate a key press
+ keybd_event(vk[k], 0x45, KEYEVENTF_EXTENDEDKEY | 0, 0);
+
+ // Simulate a key release
+ keybd_event(vk[k], 0x45, KEYEVENTF_EXTENDEDKEY | KEYEVENTF_KEYUP, 0);
+ }
+ }
+
+ keyState[VK_NUMLOCK] = (keyState[VK_NUMLOCK] & ~1) | ((state >> 0) & 1);
+ keyState[VK_CAPITAL] = (keyState[VK_CAPITAL] & ~1) | ((state >> 1) & 1);
+ keyState[VK_SCROLL] = (keyState[VK_SCROLL] & ~1) | ((state >> 2) & 1);
+ SetKeyboardState(&keyState[0]);
+ break;
+ }
+
+ case LED_METHOD_PS2:
+ {
+ KEYBOARD_INDICATOR_PARAMETERS InputBuffer; // Input buffer for DeviceIoControl
+ ULONG DataLength = sizeof(KEYBOARD_INDICATOR_PARAMETERS);
+ ULONG ReturnedLength; // Number of bytes returned in output buffer
+ UINT LedFlags=0;
+
+ // Demangle lights to match 95/98
+ if (state & 0x1) LedFlags |= KEYBOARD_NUM_LOCK_ON;
+ if (state & 0x2) LedFlags |= KEYBOARD_CAPS_LOCK_ON;
+ if (state & 0x4) LedFlags |= KEYBOARD_SCROLL_LOCK_ON;
+
+ // Address first keyboard
+ InputBuffer.UnitId = 0;
+ InputBuffer.LedFlags = LedFlags;
+ DeviceIoControl(hKbdDev, IOCTL_KEYBOARD_SET_INDICATORS,
+ &InputBuffer, DataLength,
+ NULL, 0,
+ &ReturnedLength, NULL);
+ break;
+ }
+ }
+}
diff --git a/src/osd/windows/main.c b/src/osd/windows/main.c
new file mode 100644
index 00000000000..0f4898b8104
--- /dev/null
+++ b/src/osd/windows/main.c
@@ -0,0 +1,89 @@
+//============================================================
+//
+// main.c - Win32 main program
+//
+// Copyright (c) 1996-2007, Nicola Salmoria and the MAME Team.
+// Visit http://mamedev.org for licensing and usage restrictions.
+//
+//============================================================
+
+// standard windows headers
+#define _WIN32_WINNT 0x0400
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#include <tchar.h>
+
+// MAMEOS headers
+#include "strconv.h"
+
+extern int utf8_main(int argc, char **argv);
+
+
+
+//============================================================
+// main
+//============================================================
+
+// undo the command-line #define that maps main to utf8_main in all other cases
+#ifndef WINUI
+#undef main
+#endif
+
+#ifdef __GNUC__
+int main(int argc, char **a_argv)
+#else // !__GNUC__
+int _tmain(int argc, TCHAR **argv)
+#endif // __GNUC__
+{
+ int i, rc;
+ char **utf8_argv;
+
+#ifdef __GNUC__
+ TCHAR **argv;
+#ifdef UNICODE
+ // MinGW doesn't support wmain() directly, so we have to jump through some hoops
+ extern void __wgetmainargs(int *argc, wchar_t ***wargv, wchar_t ***wenviron, int expand_wildcards, int *startupinfo);
+ WCHAR **wenviron;
+ int startupinfo;
+ __wgetmainargs(&argc, &argv, &wenviron, 0, &startupinfo);
+#else // !UNICODE
+ argv = a_argv;
+#endif // UNICODE
+#endif // __GNUC__
+
+#ifdef MALLOC_DEBUG
+{
+ extern int winalloc_in_main_code;
+ winalloc_in_main_code = TRUE;
+#endif
+
+ /* convert arguments to UTF-8 */
+ utf8_argv = (char **) malloc(argc * sizeof(*argv));
+ if (utf8_argv == NULL)
+ return 999;
+ for (i = 0; i < argc; i++)
+ {
+ utf8_argv[i] = utf8_from_tstring(argv[i]);
+ if (utf8_argv[i] == NULL)
+ return 999;
+ }
+
+ /* run utf8_main */
+ rc = utf8_main(argc, utf8_argv);
+
+ /* free arguments */
+ for (i = 0; i < argc; i++)
+ free(utf8_argv[i]);
+ free(utf8_argv);
+
+#ifdef MALLOC_DEBUG
+ {
+ void check_unfreed_mem(void);
+ check_unfreed_mem();
+ }
+ winalloc_in_main_code = FALSE;
+}
+#endif
+
+ return rc;
+}
diff --git a/src/osd/windows/mame.man b/src/osd/windows/mame.man
new file mode 100644
index 00000000000..2ea6a6b2e5a
--- /dev/null
+++ b/src/osd/windows/mame.man
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+ <assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
+ <assemblyIdentity version="1.0.0.0" processorArchitecture="*" name="MAME" type="win32" />
+ <description>Multiple Arcade Machine Emulator</description>
+ <dependency>
+ <dependentAssembly>
+ <assemblyIdentity type="win32" name="Microsoft.Windows.Common-Controls" version="6.0.0.0" publicKeyToken="6595b64144ccf1df" language="*" processorArchitecture="*"/>
+ </dependentAssembly>
+ </dependency>
+</assembly>
diff --git a/src/osd/windows/mame.rc b/src/osd/windows/mame.rc
new file mode 100644
index 00000000000..29ae8059256
--- /dev/null
+++ b/src/osd/windows/mame.rc
@@ -0,0 +1,13 @@
+//
+// mame.rc - Minimal resource file for Win32 MAME
+//
+// Copyright (c) 1996-2007, Nicola Salmoria and the MAME Team.
+// Visit http://mamedev.org for licensing and usage restrictions.
+//
+
+#define NOWINRES
+#include "windows.h"
+#include "commctrl.h"
+#include "mamevers.rc"
+
+1 24 MOVEABLE PURE "mame.man"
diff --git a/src/osd/windows/mamex64.man b/src/osd/windows/mamex64.man
new file mode 100644
index 00000000000..cc62373447b
--- /dev/null
+++ b/src/osd/windows/mamex64.man
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+ <assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
+ <assemblyIdentity version="1.0.0.0" processorArchitecture="amd64" name="MAME" type="win32" />
+ <description>Multiple Arcade Machine Emulator</description>
+ <dependency>
+ <dependentAssembly>
+ <assemblyIdentity type="win32" name="Microsoft.Windows.Common-Controls" version="6.0.0.0" publicKeyToken="6595b64144ccf1df" language="*" processorArchitecture="amd64"/>
+ </dependentAssembly>
+ </dependency>
+</assembly>
diff --git a/src/osd/windows/mamex64.rc b/src/osd/windows/mamex64.rc
new file mode 100644
index 00000000000..76290bcd355
--- /dev/null
+++ b/src/osd/windows/mamex64.rc
@@ -0,0 +1,14 @@
+//
+// mamex64.rc - Minimal resource file for Win32 MAME
+//
+// Copyright (c) 1996-2007, Nicola Salmoria and the MAME Team.
+// Visit http://mamedev.org for licensing and usage restrictions.
+//
+
+#define NOWINRES
+#include "windows.h"
+#include "commctrl.h"
+
+#include "mamevers.rc"
+
+1 24 MOVEABLE PURE "mamex64.man"
diff --git a/src/osd/windows/multidef.h b/src/osd/windows/multidef.h
new file mode 100644
index 00000000000..1a2caed5ed0
--- /dev/null
+++ b/src/osd/windows/multidef.h
@@ -0,0 +1,38 @@
+//============================================================
+//
+// multidef.h - Win32 Multi Monitors
+//
+//============================================================
+
+#ifndef __MULTIDEF__
+#define __MULTIDEF__
+
+/*
+ The MinGW implementations of windef.h and winuser.h define
+ a bunch of things that prevent the standard MS multimon.h
+ header from working as designed. This is a workaround.
+
+ Note, we can only include multimon.h exactly once. So any
+ derivative builds like MAME32 should include this file
+ instead of multimon.h
+*/
+
+#if defined(SM_CMONITORS) && (WINVER < 0x0500)
+
+#undef MONITOR_DEFAULTTONULL
+#undef MONITOR_DEFAULTTOPRIMARY
+#undef MONITOR_DEFAULTTONEAREST
+#undef MONITORINFOF_PRIMARY
+
+#define MONITOR_DEFAULTTONULL 0x00000000
+#define MONITOR_DEFAULTTOPRIMARY 0x00000001
+#define MONITOR_DEFAULTTONEAREST 0x00000002
+#define MONITORINFOF_PRIMARY 0x00000001
+
+#endif /* (SM_CMONITORS) && (WINVER < 0x0500) */
+
+
+#define COMPILE_MULTIMON_STUBS
+#include <multimon.h>
+
+#endif /* __MULTIDEF__ */
diff --git a/src/osd/windows/osd_cpu.h b/src/osd/windows/osd_cpu.h
new file mode 100644
index 00000000000..566217b4e47
--- /dev/null
+++ b/src/osd/windows/osd_cpu.h
@@ -0,0 +1,60 @@
+//============================================================
+//
+// osd_cpu.h - Win32 CPU-specific data types
+//
+// Copyright (c) 1996-2007, Nicola Salmoria and the MAME Team.
+// Visit http://mamedev.org for licensing and usage restrictions.
+//
+//============================================================
+
+/*******************************************************************************
+* *
+* Define size independent data types and operations. *
+* *
+* The following types must be supported by all platforms: *
+* *
+* UINT8 - Unsigned 8-bit Integer INT8 - Signed 8-bit integer *
+* UINT16 - Unsigned 16-bit Integer INT16 - Signed 16-bit integer *
+* UINT32 - Unsigned 32-bit Integer INT32 - Signed 32-bit integer *
+* UINT64 - Unsigned 64-bit Integer INT64 - Signed 64-bit integer *
+* *
+* *
+* The macro names for the artithmatic operations are composed as follows: *
+* *
+* XXX_R_A_B, where XXX - 3 letter operation code (ADD, SUB, etc.) *
+* R - The type of the result *
+* A - The type of operand 1 *
+* B - The type of operand 2 (if binary operation) *
+* *
+* Each type is one of: U8,8,U16,16,U32,32,U64,64 *
+* *
+*******************************************************************************/
+#pragma once
+
+#ifndef OSD_CPU_H
+#define OSD_CPU_H
+
+/* Combine two 32-bit integers into a 64-bit integer */
+#define COMBINE_64_32_32(A,B) ((((UINT64)(A))<<32) | (UINT32)(B))
+#define COMBINE_U64_U32_U32(A,B) COMBINE_64_32_32(A,B)
+
+/* Return upper 32 bits of a 64-bit integer */
+#define HI32_32_64(A) (((UINT64)(A)) >> 32)
+#define HI32_U32_U64(A) HI32_32_64(A)
+
+/* Return lower 32 bits of a 64-bit integer */
+#define LO32_32_64(A) ((A) & 0xffffffff)
+#define LO32_U32_U64(A) LO32_32_64(A)
+
+#define DIV_64_64_32(A,B) ((A)/(B))
+#define DIV_U64_U64_U32(A,B) ((A)/(UINT32)(B))
+
+#define MOD_32_64_32(A,B) ((A)%(B))
+#define MOD_U32_U64_U32(A,B) ((A)%(UINT32)(B))
+
+#define MUL_64_32_32(A,B) ((A)*(INT64)(B))
+#define MUL_U64_U32_U32(A,B) ((A)*(UINT64)(UINT32)(B))
+
+
+
+#endif /* defined OSD_CPU_H */
diff --git a/src/osd/windows/osinline.h b/src/osd/windows/osinline.h
new file mode 100644
index 00000000000..e6131724886
--- /dev/null
+++ b/src/osd/windows/osinline.h
@@ -0,0 +1,562 @@
+//============================================================
+//
+// osinline.h
+//
+// x86 inline implementations for MSVC compiler.
+//
+// Copyright (c) 1996-2007, Nicola Salmoria and the MAME Team.
+// Visit http://mamedev.org for licensing and usage restrictions.
+//
+//============================================================
+
+#ifndef __OSINLINE__
+#define __OSINLINE__
+
+#ifdef PTR64
+#include <intrin.h>
+#pragma intrinsic(_BitScanReverse)
+#else
+#pragma intrinsic(_InterlockedCompareExchange)
+#pragma intrinsic(_InterlockedExchange)
+#pragma intrinsic(_InterlockedExchangeAdd)
+#endif
+
+
+/***************************************************************************
+ INLINE MATH FUNCTIONS
+***************************************************************************/
+
+/*-------------------------------------------------
+ mul_32x32 - perform a signed 32 bit x 32 bit
+ multiply and return the full 64 bit result
+-------------------------------------------------*/
+
+#ifndef PTR64
+#define mul_32x32 _mul_32x32
+INLINE INT64 _mul_32x32(INT32 a, INT32 b)
+{
+ // in theory this should work, but it is untested
+ __asm
+ {
+ mov eax,a
+ imul b
+ // leave results in edx:eax
+ }
+}
+#endif
+
+
+/*-------------------------------------------------
+ mulu_32x32 - perform an unsigned 32 bit x
+ 32 bit multiply and return the full 64 bit
+ result
+-------------------------------------------------*/
+
+#ifndef PTR64
+#define mulu_32x32 _mulu_32x32
+INLINE UINT64 _mulu_32x32(UINT32 a, UINT32 b)
+{
+ // in theory this should work, but it is untested
+ __asm
+ {
+ mov eax,a
+ mul b
+ // leave results in edx:eax
+ }
+}
+#endif
+
+
+/*-------------------------------------------------
+ mul_32x32_hi - perform a signed 32 bit x 32 bit
+ multiply and return the upper 32 bits of the
+ result
+-------------------------------------------------*/
+
+#ifndef PTR64
+#define mul_32x32_hi _mul_32x32_hi
+INLINE INT32 _mul_32x32_hi(INT32 a, INT32 b)
+{
+ INT32 result;
+
+ __asm
+ {
+ mov eax,a
+ imul b
+ mov result,edx
+ }
+
+ return result;
+}
+#endif
+
+
+/*-------------------------------------------------
+ mulu_32x32_hi - perform an unsigned 32 bit x
+ 32 bit multiply and return the upper 32 bits
+ of the result
+-------------------------------------------------*/
+
+#ifndef PTR64
+#define mulu_32x32_hi _mulu_32x32_hi
+INLINE UINT32 _mulu_32x32_hi(UINT32 a, UINT32 b)
+{
+ INT32 result;
+
+ __asm
+ {
+ mov eax,a
+ mul b
+ mov result,edx
+ }
+
+ return result;
+}
+#endif
+
+
+/*-------------------------------------------------
+ mul_32x32_shift - perform a signed 32 bit x
+ 32 bit multiply and shift the result by the
+ given number of bits before truncating the
+ result to 32 bits
+-------------------------------------------------*/
+
+#ifndef PTR64
+#define mul_32x32_shift _mul_32x32_shift
+INLINE INT32 _mul_32x32_shift(INT32 a, INT32 b, UINT8 shift)
+{
+ INT32 result;
+
+ __asm
+ {
+ mov eax,a
+ imul b
+ mov cl,shift
+ shrd eax,edx,cl
+ mov result,eax
+ }
+
+ return result;
+}
+#endif
+
+
+/*-------------------------------------------------
+ mulu_32x32_shift - perform an unsigned 32 bit x
+ 32 bit multiply and shift the result by the
+ given number of bits before truncating the
+ result to 32 bits
+-------------------------------------------------*/
+
+#ifndef PTR64
+#define mulu_32x32_shift _mulu_32x32_shift
+INLINE UINT32 _mulu_32x32_shift(UINT32 a, UINT32 b, UINT8 shift)
+{
+ INT32 result;
+
+ __asm
+ {
+ mov eax,a
+ mul b
+ mov cl,shift
+ shrd eax,edx,cl
+ mov result,eax
+ }
+
+ return result;
+}
+#endif
+
+
+/*-------------------------------------------------
+ div_64x32 - perform a signed 64 bit x 32 bit
+ divide and return the 32 bit quotient
+-------------------------------------------------*/
+
+#ifndef PTR64
+#define div_64x32 _div_64x32
+INLINE INT32 _div_64x32(INT64 a, INT32 b)
+{
+ INT32 result;
+ INT32 alow = a;
+ INT32 ahigh = a >> 32;
+
+ __asm
+ {
+ mov eax,alow
+ mov edx,ahigh
+ idiv b
+ mov result,eax
+ }
+
+ return result;
+}
+#endif
+
+
+/*-------------------------------------------------
+ divu_64x32 - perform an unsigned 64 bit x 32 bit
+ divide and return the 32 bit quotient
+-------------------------------------------------*/
+
+#ifndef PTR64
+#define divu_64x32 _divu_64x32
+INLINE UINT32 _divu_64x32(UINT64 a, UINT32 b)
+{
+ UINT32 result;
+ UINT32 alow = a;
+ UINT32 ahigh = a >> 32;
+
+ __asm
+ {
+ mov eax,alow
+ mov edx,ahigh
+ div b
+ mov result,eax
+ }
+
+ return result;
+}
+#endif
+
+
+/*-------------------------------------------------
+ div_64x32_rem - perform a signed 64 bit x 32
+ bit divide and return the 32 bit quotient and
+ 32 bit remainder
+-------------------------------------------------*/
+
+#ifndef PTR64
+#define div_64x32_rem _div_64x32_rem
+INLINE INT32 _div_64x32_rem(INT64 a, INT32 b, INT32 *remainder)
+{
+ INT32 result;
+ INT32 alow = a;
+ INT32 ahigh = a >> 32;
+ INT32 rem;
+
+ __asm
+ {
+ mov eax,alow
+ mov edx,ahigh
+ idiv b
+ mov result,eax
+ mov rem,edx
+ }
+
+ *remainder = rem;
+ return result;
+}
+#endif
+
+
+/*-------------------------------------------------
+ divu_64x32_rem - perform an unsigned 64 bit x
+ 32 bit divide and return the 32 bit quotient
+ and 32 bit remainder
+-------------------------------------------------*/
+
+#ifndef PTR64
+#define divu_64x32_rem _divu_64x32_rem
+INLINE UINT32 _divu_64x32_rem(UINT64 a, UINT32 b, UINT32 *remainder)
+{
+ UINT32 result;
+ UINT32 alow = a;
+ UINT32 ahigh = a >> 32;
+ UINT32 rem;
+
+ __asm
+ {
+ mov eax,alow
+ mov edx,ahigh
+ div b
+ mov result,eax
+ mov rem,edx
+ }
+
+ *remainder = rem;
+ return result;
+}
+#endif
+
+
+/*-------------------------------------------------
+ div_32x32_shift - perform a signed divide of
+ two 32 bit values, shifting the first before
+ division, and returning the 32 bit quotient
+-------------------------------------------------*/
+
+#ifndef PTR64
+#define div_32x32_shift _div_32x32_shift
+INLINE INT32 _div_32x32_shift(INT32 a, INT32 b, UINT8 shift)
+{
+ INT32 result;
+
+ __asm
+ {
+ mov eax,a
+ cdq
+ mov cl,shift
+ shld edx,eax,cl
+ shl eax,cl
+ idiv b
+ mov result,eax
+ }
+
+ return result;
+}
+#endif
+
+
+/*-------------------------------------------------
+ divu_32x32_shift - perform an unsigned divide of
+ two 32 bit values, shifting the first before
+ division, and returning the 32 bit quotient
+-------------------------------------------------*/
+
+#ifndef PTR64
+#define divu_32x32_shift _divu_32x32_shift
+INLINE UINT32 _divu_32x32_shift(UINT32 a, UINT32 b, UINT8 shift)
+{
+ UINT32 result;
+
+ __asm
+ {
+ mov eax,a
+ xor edx,edx
+ mov cl,shift
+ shld edx,eax,cl
+ shl eax,cl
+ div b
+ mov result,eax
+ }
+
+ return result;
+}
+#endif
+
+
+/*-------------------------------------------------
+ mod_64x32 - perform a signed 64 bit x 32 bit
+ divide and return the 32 bit remainder
+-------------------------------------------------*/
+
+#ifndef PTR64
+#define mod_64x32 _mod_64x32
+INLINE INT32 _mod_64x32(INT64 a, INT32 b)
+{
+ INT32 result;
+ INT32 alow = a;
+ INT32 ahigh = a >> 32;
+
+ __asm
+ {
+ mov eax,alow
+ mov edx,ahigh
+ idiv b
+ mov result,edx
+ }
+
+ return result;
+}
+#endif
+
+
+/*-------------------------------------------------
+ modu_64x32 - perform an unsigned 64 bit x 32 bit
+ divide and return the 32 bit remainder
+-------------------------------------------------*/
+
+#ifndef PTR64
+#define modu_64x32 _modu_64x32
+INLINE UINT32 _modu_64x32(UINT64 a, UINT32 b)
+{
+ UINT32 result;
+ UINT32 alow = a;
+ UINT32 ahigh = a >> 32;
+
+ __asm
+ {
+ mov eax,alow
+ mov edx,ahigh
+ div b
+ mov result,edx
+ }
+
+ return result;
+}
+#endif
+
+
+/*-------------------------------------------------
+ recip_approx - compute an approximate floating
+ point reciprocal
+-------------------------------------------------*/
+
+#ifdef PTR64
+#define recip_approx _recip_approx
+INLINE float _recip_approx(float z)
+{
+ __m128 mz = _mm_set_ss(z);
+ __m128 mooz = _mm_rcp_ss(mz);
+ float ooz;
+ _mm_store_ss(&ooz, mooz);
+ return ooz;
+}
+#endif
+
+
+
+/***************************************************************************
+ INLINE BIT MANIPULATION FUNCTIONS
+***************************************************************************/
+
+/*-------------------------------------------------
+ count_leading_zeros - return the number of
+ leading zero bits in a 32-bit value
+-------------------------------------------------*/
+
+#define count_leading_zeros _count_leading_zeros
+INLINE UINT8 _count_leading_zeros(UINT32 value)
+{
+#ifdef PTR64
+ UINT32 index;
+ return _BitScanReverse(&index, value) ? (index ^ 31) : 32;
+#else
+ INT32 result;
+
+ __asm
+ {
+ bsr eax,value
+ jnz skip
+ mov eax,63
+ skip:
+ xor eax,31
+ mov result,eax
+ }
+
+ return result;
+#endif
+}
+
+
+/*-------------------------------------------------
+ count_leading_ones - return the number of
+ leading one bits in a 32-bit value
+-------------------------------------------------*/
+
+#define count_leading_ones _count_leading_ones
+INLINE UINT8 _count_leading_ones(UINT32 value)
+{
+#ifdef PTR64
+ UINT32 index;
+ return _BitScanReverse(&index, ~value) ? (index ^ 31) : 32;
+#else
+ INT32 result;
+
+ __asm
+ {
+ mov eax,value
+ not eax
+ bsr eax,eax
+ jnz skip
+ mov eax,63
+ skip:
+ xor eax,31
+ mov result,eax
+ }
+
+ return result;
+#endif
+}
+
+
+
+/***************************************************************************
+ INLINE SYNCHRONIZATION FUNCTIONS
+***************************************************************************/
+
+/*-------------------------------------------------
+ compare_exchange32 - compare the 'compare'
+ value against the memory at 'ptr'; if equal,
+ swap in the 'exchange' value. Regardless,
+ return the previous value at 'ptr'.
+-------------------------------------------------*/
+
+#define compare_exchange32 _compare_exchange32
+INLINE INT32 _compare_exchange32(INT32 volatile *ptr, INT32 compare, INT32 exchange)
+{
+ return _InterlockedCompareExchange(ptr, exchange, compare);
+}
+
+
+/*-------------------------------------------------
+ compare_exchange64 - compare the 'compare'
+ value against the memory at 'ptr'; if equal,
+ swap in the 'exchange' value. Regardless,
+ return the previous value at 'ptr'.
+-------------------------------------------------*/
+
+#ifdef PTR64
+#define compare_exchange64 _compare_exchange64
+INLINE INT64 _compare_exchange64(INT64 volatile *ptr, INT64 compare, INT64 exchange)
+{
+ return _InterlockedCompareExchange64(ptr, exchange, compare);
+}
+#endif
+
+
+/*-------------------------------------------------
+ atomic_exchange32 - atomically exchange the
+ exchange value with the memory at 'ptr',
+ returning the original value.
+-------------------------------------------------*/
+
+#define atomic_exchange32 _atomic_exchange32
+INLINE INT32 _atomic_exchange32(INT32 volatile *ptr, INT32 exchange)
+{
+ return _InterlockedExchange(ptr, exchange);
+}
+
+
+/*-------------------------------------------------
+ atomic_add32 - atomically add the delta value
+ to the memory at 'ptr', returning the final
+ result.
+-------------------------------------------------*/
+
+#define atomic_add32 _atomic_add32
+INLINE INT32 _atomic_add32(INT32 volatile *ptr, INT32 delta)
+{
+ return _InterlockedExchangeAdd(ptr, delta) + delta;
+}
+
+
+/*-------------------------------------------------
+ atomic_increment32 - atomically increment the
+ 32-bit value in memory at 'ptr', returning the
+ final result.
+-------------------------------------------------*/
+
+#define atomic_increment32 _atomic_increment32
+INLINE INT32 _atomic_increment32(INT32 volatile *ptr)
+{
+ return _InterlockedIncrement(ptr);
+}
+
+
+/*-------------------------------------------------
+ atomic_decrement32 - atomically decrement the
+ 32-bit value in memory at 'ptr', returning the
+ final result.
+-------------------------------------------------*/
+
+#define atomic_decrement32 _atomic_decrement32
+INLINE INT32 _atomic_decrement32(INT32 volatile *ptr)
+{
+ return _InterlockedDecrement(ptr);
+}
+
+
+#endif /* __OSINLINE__ */
diff --git a/src/osd/windows/output.c b/src/osd/windows/output.c
new file mode 100644
index 00000000000..c4511900ccd
--- /dev/null
+++ b/src/osd/windows/output.c
@@ -0,0 +1,311 @@
+//============================================================
+//
+// output.c - Win32 implementation of MAME output routines
+//
+// Copyright (c) 1996-2007, Nicola Salmoria and the MAME Team.
+// Visit http://mamedev.org for licensing and usage restrictions.
+//
+//============================================================
+
+// standard windows headers
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+
+// MAME headers
+#include "osdepend.h"
+#include "driver.h"
+
+// MAMEOS headers
+#include "output.h"
+
+
+
+//============================================================
+// CONSTANTS
+//============================================================
+
+// window styles
+#define WINDOW_STYLE WS_OVERLAPPEDWINDOW
+#define WINDOW_STYLE_EX 0
+
+
+
+//============================================================
+// TYPEDEFS
+//============================================================
+
+typedef struct _registered_client registered_client;
+struct _registered_client
+{
+ registered_client * next; // next client in the list
+ LPARAM id; // client-specified ID
+ HWND hwnd; // client HWND
+};
+
+
+
+//============================================================
+// GLOBAL VARIABLES
+//============================================================
+
+// our HWND
+static HWND output_hwnd;
+
+// client list
+static registered_client * clientlist;
+
+// message IDs
+static UINT om_mame_start;
+static UINT om_mame_stop;
+static UINT om_mame_update_state;
+static UINT om_mame_register_client;
+static UINT om_mame_unregister_client;
+static UINT om_mame_get_id_string;
+
+
+
+//============================================================
+// FUNCTION PROTOTYPES
+//============================================================
+
+static void winoutput_exit(running_machine *machine);
+static int create_window_class(void);
+static LRESULT CALLBACK output_window_proc(HWND wnd, UINT message, WPARAM wparam, LPARAM lparam);
+static LRESULT register_client(HWND hwnd, LPARAM id);
+static LRESULT unregister_client(HWND hwnd, LPARAM id);
+static LRESULT send_id_string(HWND hwnd, LPARAM id);
+static void notifier_callback(const char *outname, INT32 value, void *param);
+
+
+
+//============================================================
+// winoutput_init
+//============================================================
+
+void winoutput_init(running_machine *machine)
+{
+ int result;
+
+ // ensure we get cleaned up
+ add_exit_callback(machine, winoutput_exit);
+
+ // reset globals
+ clientlist = NULL;
+
+ // create our window class
+ result = create_window_class();
+ assert(result == 0);
+
+ // create a window
+ output_hwnd = CreateWindowEx(
+ WINDOW_STYLE_EX,
+ OUTPUT_WINDOW_CLASS,
+ OUTPUT_WINDOW_NAME,
+ WINDOW_STYLE,
+ 0, 0,
+ 1, 1,
+ NULL,
+ NULL,
+ GetModuleHandle(NULL),
+ NULL);
+ assert(output_hwnd != NULL);
+
+ // allocate message ids
+ om_mame_start = RegisterWindowMessage(OM_MAME_START);
+ assert(om_mame_start != 0);
+ om_mame_stop = RegisterWindowMessage(OM_MAME_STOP);
+ assert(om_mame_stop != 0);
+ om_mame_update_state = RegisterWindowMessage(OM_MAME_UPDATE_STATE);
+ assert(om_mame_update_state != 0);
+
+ om_mame_register_client = RegisterWindowMessage(OM_MAME_REGISTER_CLIENT);
+ assert(om_mame_register_client != 0);
+ om_mame_unregister_client = RegisterWindowMessage(OM_MAME_UNREGISTER_CLIENT);
+ assert(om_mame_unregister_client != 0);
+ om_mame_get_id_string = RegisterWindowMessage(OM_MAME_GET_ID_STRING);
+ assert(om_mame_get_id_string != 0);
+
+ // broadcast a startup message
+ PostMessage(HWND_BROADCAST, om_mame_start, (WPARAM)output_hwnd, 0);
+
+ // register a notifier for output changes
+ output_set_notifier(NULL, notifier_callback, NULL);
+}
+
+
+//============================================================
+// winoutput_exit
+//============================================================
+
+static void winoutput_exit(running_machine *machine)
+{
+ // free all the clients
+ while (clientlist != NULL)
+ {
+ registered_client *temp = clientlist;
+ clientlist = temp->next;
+ free(temp);
+ }
+
+ // broadcast a shutdown message
+ PostMessage(HWND_BROADCAST, om_mame_stop, (WPARAM)output_hwnd, 0);
+}
+
+
+//============================================================
+// create_window_class
+//============================================================
+
+static int create_window_class(void)
+{
+ static UINT8 classes_created = FALSE;
+
+ /* only do this once */
+ if (!classes_created)
+ {
+ WNDCLASS wc = { 0 };
+
+ // initialize the description of the window class
+ wc.lpszClassName = OUTPUT_WINDOW_CLASS;
+ wc.hInstance = GetModuleHandle(NULL);
+ wc.lpfnWndProc = output_window_proc;
+
+ // register the class; fail if we can't
+ if (!RegisterClass(&wc))
+ return 1;
+ classes_created = TRUE;
+ }
+
+ return 0;
+}
+
+
+//============================================================
+// output_window_proc
+//============================================================
+
+static LRESULT CALLBACK output_window_proc(HWND wnd, UINT message, WPARAM wparam, LPARAM lparam)
+{
+ // register a new client
+ if (message == om_mame_register_client)
+ return register_client((HWND)wparam, lparam);
+
+ // unregister a client
+ else if (message == om_mame_unregister_client)
+ return unregister_client((HWND)wparam, lparam);
+
+ // get a string for an ID
+ else if (message == om_mame_get_id_string)
+ return send_id_string((HWND)wparam, lparam);
+
+ else
+ return DefWindowProc(wnd, message, wparam, lparam);
+}
+
+
+//============================================================
+// register_client
+//============================================================
+
+static LRESULT register_client(HWND hwnd, LPARAM id)
+{
+ registered_client **client;
+
+ // find the end of the list; if we find ourself already registered,
+ // return 1
+ for (client = &clientlist; *client != NULL; client = &(*client)->next)
+ if ((*client)->id == id)
+ {
+ (*client)->hwnd = hwnd;
+ output_notify_all(notifier_callback, *client);
+ return 1;
+ }
+
+ // add us to the end
+ *client = malloc_or_die(sizeof(**client));
+ (*client)->next = NULL;
+ (*client)->id = id;
+ (*client)->hwnd = hwnd;
+
+ // request a notification for all outputs
+ output_notify_all(notifier_callback, *client);
+ return 0;
+}
+
+
+//============================================================
+// unregister_client
+//============================================================
+
+static LRESULT unregister_client(HWND hwnd, LPARAM id)
+{
+ registered_client **client;
+ int found = FALSE;
+
+ // find any matching IDs in the list and remove them
+ for (client = &clientlist; *client != NULL; client = &(*client)->next)
+ if ((*client)->id == id)
+ {
+ registered_client *temp = *client;
+ *client = (*client)->next;
+ free(temp);
+ found = TRUE;
+ }
+
+ // return an error if not found
+ return found ? 0 : 1;
+}
+
+
+//============================================================
+// send_id_string
+//============================================================
+
+static LRESULT send_id_string(HWND hwnd, LPARAM id)
+{
+ copydata_id_string *temp;
+ COPYDATASTRUCT copydata;
+ const char *name;
+ int datalen;
+
+ // id 0 is the name of the game
+ if (id == 0)
+ name = Machine->gamedrv->name;
+ else
+ name = output_id_to_name(id);
+
+ // a NULL name is an empty string
+ if (name == NULL)
+ name = "";
+
+ // allocate memory for the message
+ datalen = sizeof(*temp) + strlen(name);
+ temp = malloc_or_die(datalen);
+ temp->id = id;
+ strcpy(temp->string, name);
+
+ // reply by using SendMessage with WM_COPYDATA
+ copydata.dwData = COPYDATA_MESSAGE_ID_STRING;
+ copydata.cbData = datalen;
+ copydata.lpData = temp;
+ SendMessage(hwnd, WM_COPYDATA, (WPARAM)output_hwnd, (LPARAM)&copydata);
+
+ // free the data
+ free(temp);
+ return 0;
+}
+
+
+//============================================================
+// send_id_string
+//============================================================
+
+static void notifier_callback(const char *outname, INT32 value, void *param)
+{
+ registered_client *client;
+
+ // loop over clients and notify them
+ for (client = clientlist; client != NULL; client = client->next)
+ if (param == NULL || param == client)
+ PostMessage(client->hwnd, om_mame_update_state, output_name_to_id(outname), value);
+}
diff --git a/src/osd/windows/output.h b/src/osd/windows/output.h
new file mode 100644
index 00000000000..e147e836aea
--- /dev/null
+++ b/src/osd/windows/output.h
@@ -0,0 +1,97 @@
+//============================================================
+//
+// output.h - Win32 implementation of MAME output routines
+//
+// Copyright (c) 1996-2007, Nicola Salmoria and the MAME Team.
+// Visit http://mamedev.org for licensing and usage restrictions.
+//
+//============================================================
+
+#ifndef __WINDOWS_OUTPUT_H__
+#define __WINDOWS_OUTPUT_H__
+
+
+//============================================================
+// CONSTANTS
+//============================================================
+
+// window parameters
+#define OUTPUT_WINDOW_CLASS TEXT("MAMEOutput")
+#define OUTPUT_WINDOW_NAME TEXT("MAMEOutput")
+
+//
+// These messages are sent by MAME:
+//
+
+// OM_MAME_START: broadcast when MAME initializes
+// WPARAM = HWND of MAME's output window
+// LPARAM = unused
+#define OM_MAME_START TEXT("MAMEOutputStart")
+
+// OM_MAME_STOP: broadcast when MAME shuts down
+// WPARAM = HWND of MAME's output window
+// LPARAM = unused
+#define OM_MAME_STOP TEXT("MAMEOutputStop")
+
+// OM_MAME_UPDATE_STATE: sent to registered clients when the state
+// of an output changes
+// WPARAM = ID of the output
+// LPARAM = new value for the output
+#define OM_MAME_UPDATE_STATE TEXT("MAMEOutputUpdateState")
+
+
+//
+// These messages are sent by external clients to MAME:
+//
+
+// OM_MAME_REGISTER_CLIENT: sent to MAME to register a client
+// WPARAM = HWND of client's listener window
+// LPARAM = client-specified ID (must be unique)
+#define OM_MAME_REGISTER_CLIENT TEXT("MAMEOutputRegister")
+
+// OM_MAME_UNREGISTER_CLIENT: sent to MAME to unregister a client
+// WPARAM = HWND of client's listener window
+// LPARAM = client-specified ID (must match registration)
+#define OM_MAME_UNREGISTER_CLIENT TEXT("MAMEOutputUnregister")
+
+// OM_MAME_GET_ID_STRING: requests the string associated with a
+// given ID. ID=0 is always the name of the game. Other IDs are
+// only discoverable from a OM_MAME_UPDATE_STATE message. The
+// result will be sent back as a WM_COPYDATA message with MAME's
+// output window as the sender, dwData = the ID of the string,
+// and lpData pointing to a NULL-terminated string.
+// WPARAM = HWND of client's listener window
+// LPARAM = ID you wish to know about
+#define OM_MAME_GET_ID_STRING TEXT("MAMEOutputGetIDString")
+
+
+//
+// These constants are used to identify WM_COPYDATA messages
+// coming from MAME:
+//
+
+#define COPYDATA_MESSAGE_ID_STRING 1
+
+
+
+//============================================================
+// TYPE DEFINITIONS
+//============================================================
+
+typedef struct _copydata_id_string copydata_id_string;
+struct _copydata_id_string
+{
+ UINT32 id; // ID that was requested
+ char string[1]; // string array containing the data
+};
+
+
+
+//============================================================
+// FUNCTION PROTOTYPES
+//============================================================
+
+void winoutput_init(running_machine *machine);
+
+
+#endif /* __WINDOWS_OUTPUT_H__ */
diff --git a/src/osd/windows/sound.c b/src/osd/windows/sound.c
new file mode 100644
index 00000000000..ca41344c69d
--- /dev/null
+++ b/src/osd/windows/sound.c
@@ -0,0 +1,427 @@
+//============================================================
+//
+// sound.c - Win32 implementation of MAME sound routines
+//
+// Copyright (c) 1996-2007, Nicola Salmoria and the MAME Team.
+// Visit http://mamedev.org for licensing and usage restrictions.
+//
+//============================================================
+
+// standard windows headers
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#include <mmsystem.h>
+
+// undef WINNT for dsound.h to prevent duplicate definition
+#undef WINNT
+#include <dsound.h>
+
+// MAME headers
+#include "osdepend.h"
+#include "driver.h"
+#include "osdepend.h"
+
+// MAMEOS headers
+#include "winmain.h"
+#include "window.h"
+#include "video.h"
+#include "config.h"
+#include "sound.h"
+
+
+//============================================================
+// DEBUGGING
+//============================================================
+
+#define LOG_SOUND 0
+
+
+
+//============================================================
+// LOCAL VARIABLES
+//============================================================
+
+// DirectSound objects
+static LPDIRECTSOUND dsound;
+static DSCAPS dsound_caps;
+
+// sound buffers
+static LPDIRECTSOUNDBUFFER primary_buffer;
+static LPDIRECTSOUNDBUFFER stream_buffer;
+static UINT32 stream_buffer_size;
+static UINT32 stream_buffer_in;
+
+// descriptors and formats
+static DSBUFFERDESC primary_desc;
+static DSBUFFERDESC stream_desc;
+static WAVEFORMATEX primary_format;
+static WAVEFORMATEX stream_format;
+
+// buffer over/underflow counts
+static int buffer_underflows;
+static int buffer_overflows;
+
+// debugging
+#if LOG_SOUND
+static FILE * sound_log;
+#endif
+
+
+
+//============================================================
+// PROTOTYPES
+//============================================================
+
+static void sound_exit(running_machine *machine);
+static HRESULT dsound_init(void);
+static void dsound_kill(void);
+static HRESULT dsound_create_buffers(void);
+static void dsound_destroy_buffers(void);
+
+
+
+//============================================================
+// winsound_init
+//============================================================
+
+void winsound_init(running_machine *machine)
+{
+#if LOG_SOUND
+ sound_log = fopen("sound.log", "w");
+#endif
+
+ // if no sound, don't create anything
+ if (!options_get_bool(mame_options(), OPTION_SOUND))
+ return;
+
+ // ensure we get called on the way out
+ add_exit_callback(machine, sound_exit);
+
+ // attempt to initialize directsound
+ // don't make it fatal if we can't -- we'll just run without sound
+ dsound_init();
+}
+
+
+//============================================================
+// sound_exit
+//============================================================
+
+static void sound_exit(running_machine *machine)
+{
+ // kill the buffers and dsound
+ dsound_destroy_buffers();
+ dsound_kill();
+
+ // print out over/underflow stats
+ if (buffer_overflows || buffer_underflows)
+ mame_printf_verbose("Sound: buffer overflows=%d underflows=%d\n", buffer_overflows, buffer_underflows);
+
+#if LOG_SOUND
+ if (sound_log)
+ fprintf(sound_log, "Sound buffer: overflows=%d underflows=%d\n", buffer_overflows, buffer_underflows);
+ fclose(sound_log);
+#endif
+}
+
+
+//============================================================
+// copy_sample_data
+//============================================================
+
+static void copy_sample_data(INT16 *data, int bytes_to_copy)
+{
+ void *buffer1, *buffer2;
+ DWORD length1, length2;
+ HRESULT result;
+ int cur_bytes;
+
+ // attempt to lock the stream buffer
+ result = IDirectSoundBuffer_Lock(stream_buffer, stream_buffer_in, bytes_to_copy, &buffer1, &length1, &buffer2, &length2, 0);
+
+ // if we failed, assume it was an underflow (i.e.,
+ if (result != DS_OK)
+ {
+ buffer_underflows++;
+ return;
+ }
+
+ // adjust the input pointer
+ stream_buffer_in = (stream_buffer_in + bytes_to_copy) % stream_buffer_size;
+
+ // copy the first chunk
+ cur_bytes = (bytes_to_copy > length1) ? length1 : bytes_to_copy;
+ memcpy(buffer1, data, cur_bytes);
+
+ // adjust for the number of bytes
+ bytes_to_copy -= cur_bytes;
+ data = (INT16 *)((UINT8 *)data + cur_bytes);
+
+ // copy the second chunk
+ if (bytes_to_copy != 0)
+ {
+ cur_bytes = (bytes_to_copy > length2) ? length2 : bytes_to_copy;
+ memcpy(buffer2, data, cur_bytes);
+ }
+
+ // unlock
+ result = IDirectSoundBuffer_Unlock(stream_buffer, buffer1, length1, buffer2, length2);
+}
+
+
+//============================================================
+// osd_update_audio_stream
+//============================================================
+
+void osd_update_audio_stream(INT16 *buffer, int samples_this_frame)
+{
+ int bytes_this_frame = samples_this_frame * stream_format.nBlockAlign;
+ DWORD play_position, write_position;
+ HRESULT result;
+
+ // if no sound, there is no buffer
+ if (stream_buffer == NULL)
+ return;
+
+ // determine the current play position
+ result = IDirectSoundBuffer_GetCurrentPosition(stream_buffer, &play_position, &write_position);
+ if (result == DS_OK)
+ {
+ DWORD stream_in;
+
+//DWORD orig_write = write_position;
+ // normalize the write position so it is always after the play position
+ if (write_position < play_position)
+ write_position += stream_buffer_size;
+
+ // normalize the stream in position so it is always after the write position
+ stream_in = stream_buffer_in;
+ if (stream_in < write_position)
+ stream_in += stream_buffer_size;
+
+ // now we should have, in order:
+ // <------pp---wp---si--------------->
+
+ // if we're between play and write positions, then bump forward, but only in full chunks
+ while (stream_in < write_position)
+ {
+//logerror("Underflow: PP=%d WP=%d(%d) SI=%d(%d) BTF=%d\n", (int)play_position, (int)write_position, (int)orig_write, (int)stream_in, (int)stream_buffer_in, (int)bytes_this_frame);
+ buffer_underflows++;
+ stream_in += samples_this_frame;
+ }
+
+ // if we're going to overlap the play position, just skip this chunk
+ if (stream_in + bytes_this_frame > play_position + stream_buffer_size)
+ {
+//logerror("Overflow: PP=%d WP=%d(%d) SI=%d(%d) BTF=%d\n", (int)play_position, (int)write_position, (int)orig_write, (int)stream_in, (int)stream_buffer_in, (int)bytes_this_frame);
+ buffer_overflows++;
+ return;
+ }
+
+ // now we know where to copy; let's do it
+ stream_buffer_in = stream_in % stream_buffer_size;
+ copy_sample_data(buffer, bytes_this_frame);
+ }
+}
+
+
+//============================================================
+// osd_set_mastervolume
+//============================================================
+
+void osd_set_mastervolume(int attenuation)
+{
+ // clamp the attenuation to 0-32 range
+ if (attenuation > 0)
+ attenuation = 0;
+ if (attenuation < -32)
+ attenuation = -32;
+
+ // set the master volume
+ if (stream_buffer != NULL)
+ IDirectSoundBuffer_SetVolume(stream_buffer, (attenuation == -32) ? DSBVOLUME_MIN : attenuation * 100);
+}
+
+
+//============================================================
+// dsound_init
+//============================================================
+
+static HRESULT dsound_init(void)
+{
+ HRESULT result;
+
+ // create the DirectSound object
+ result = DirectSoundCreate(NULL, &dsound, NULL);
+ if (result != DS_OK)
+ {
+ mame_printf_error("Error creating DirectSound: %08x\n", (UINT32)result);
+ goto error;
+ }
+
+ // get the capabilities
+ dsound_caps.dwSize = sizeof(dsound_caps);
+ result = IDirectSound_GetCaps(dsound, &dsound_caps);
+ if (result != DS_OK)
+ {
+ mame_printf_error("Error getting DirectSound capabilities: %08x\n", (UINT32)result);
+ goto error;
+ }
+
+ // set the cooperative level
+ result = IDirectSound_SetCooperativeLevel(dsound, win_window_list->hwnd, DSSCL_PRIORITY);
+ if (result != DS_OK)
+ {
+ mame_printf_error("Error setting DirectSound cooperative level: %08x\n", (UINT32)result);
+ goto error;
+ }
+
+ // make a format description for what we want
+ stream_format.wBitsPerSample = 16;
+ stream_format.wFormatTag = WAVE_FORMAT_PCM;
+ stream_format.nChannels = 2;
+ stream_format.nSamplesPerSec = Machine->sample_rate;
+ stream_format.nBlockAlign = stream_format.wBitsPerSample * stream_format.nChannels / 8;
+ stream_format.nAvgBytesPerSec = stream_format.nSamplesPerSec * stream_format.nBlockAlign;
+
+ // compute the buffer size based on the output sample rate
+ stream_buffer_size = stream_format.nSamplesPerSec * stream_format.nBlockAlign * options_get_int(mame_options(), WINOPTION_AUDIO_LATENCY) / 10;
+ stream_buffer_size = (stream_buffer_size / 1024) * 1024;
+ if (stream_buffer_size < 1024)
+ stream_buffer_size = 1024;
+#if LOG_SOUND
+ fprintf(sound_log, "stream_buffer_size = %d\n", stream_buffer_size);
+#endif
+
+ // create the buffers
+ result = dsound_create_buffers();
+ if (result != DS_OK)
+ goto error;
+
+ // start playing
+ result = IDirectSoundBuffer_Play(stream_buffer, 0, 0, DSBPLAY_LOOPING);
+ if (result != DS_OK)
+ {
+ mame_printf_error("Error playing: %08x\n", (UINT32)result);
+ goto error;
+ }
+ return DS_OK;
+
+ // error handling
+error:
+ dsound_destroy_buffers();
+ dsound_kill();
+ return result;
+}
+
+
+//============================================================
+// dsound_kill
+//============================================================
+
+static void dsound_kill(void)
+{
+ // release the object
+ if (dsound != NULL)
+ IDirectSound_Release(dsound);
+ dsound = NULL;
+}
+
+
+//============================================================
+// dsound_create_buffers
+//============================================================
+
+static HRESULT dsound_create_buffers(void)
+{
+ HRESULT result;
+ void *buffer;
+ DWORD locked;
+
+ // create a buffer desc for the primary buffer
+ memset(&primary_desc, 0, sizeof(primary_desc));
+ primary_desc.dwSize = sizeof(primary_desc);
+ primary_desc.dwFlags = DSBCAPS_PRIMARYBUFFER | DSBCAPS_GETCURRENTPOSITION2;
+ primary_desc.lpwfxFormat = NULL;
+
+ // create the primary buffer
+ result = IDirectSound_CreateSoundBuffer(dsound, &primary_desc, &primary_buffer, NULL);
+ if (result != DS_OK)
+ {
+ mame_printf_error("Error creating primary DirectSound buffer: %08x\n", (UINT32)result);
+ goto error;
+ }
+
+ // attempt to set the primary format
+ result = IDirectSoundBuffer_SetFormat(primary_buffer, &stream_format);
+ if (result != DS_OK)
+ {
+ mame_printf_error("Error setting primary DirectSound buffer format: %08x\n", (UINT32)result);
+ goto error;
+ }
+
+ // get the primary format
+ result = IDirectSoundBuffer_GetFormat(primary_buffer, &primary_format, sizeof(primary_format), NULL);
+ if (result != DS_OK)
+ {
+ mame_printf_error("Error getting primary DirectSound buffer format: %08x\n", (UINT32)result);
+ goto error;
+ }
+ mame_printf_verbose("DirectSound: Primary buffer: %d Hz, %d bits, %d channels\n",
+ (int)primary_format.nSamplesPerSec, (int)primary_format.wBitsPerSample, (int)primary_format.nChannels);
+
+ // create a buffer desc for the stream buffer
+ memset(&stream_desc, 0, sizeof(stream_desc));
+ stream_desc.dwSize = sizeof(stream_desc);
+ stream_desc.dwFlags = DSBCAPS_CTRLVOLUME | DSBCAPS_GLOBALFOCUS | DSBCAPS_GETCURRENTPOSITION2;
+ stream_desc.dwBufferBytes = stream_buffer_size;
+ stream_desc.lpwfxFormat = &stream_format;
+
+ // create the stream buffer
+ result = IDirectSound_CreateSoundBuffer(dsound, &stream_desc, &stream_buffer, NULL);
+ if (result != DS_OK)
+ {
+ mame_printf_error("Error creating DirectSound stream buffer: %08x\n", (UINT32)result);
+ goto error;
+ }
+
+ // lock the buffer
+ result = IDirectSoundBuffer_Lock(stream_buffer, 0, stream_buffer_size, &buffer, &locked, NULL, NULL, 0);
+ if (result != DS_OK)
+ {
+ mame_printf_error("Error locking DirectSound stream buffer: %08x\n", (UINT32)result);
+ goto error;
+ }
+
+ // clear the buffer and unlock it
+ memset(buffer, 0, locked);
+ IDirectSoundBuffer_Unlock(stream_buffer, buffer, locked, NULL, 0);
+ return DS_OK;
+
+ // error handling
+error:
+ dsound_destroy_buffers();
+ return result;
+}
+
+
+//============================================================
+// dsound_destroy_buffers
+//============================================================
+
+static void dsound_destroy_buffers(void)
+{
+ // stop any playback
+ if (stream_buffer != NULL)
+ IDirectSoundBuffer_Stop(stream_buffer);
+
+ // release the stream buffer
+ if (stream_buffer != NULL)
+ IDirectSoundBuffer_Release(stream_buffer);
+ stream_buffer = NULL;
+
+ // release the primary buffer
+ if (primary_buffer != NULL)
+ IDirectSoundBuffer_Release(primary_buffer);
+ primary_buffer = NULL;
+}
diff --git a/src/osd/windows/sound.h b/src/osd/windows/sound.h
new file mode 100644
index 00000000000..79c70c59bad
--- /dev/null
+++ b/src/osd/windows/sound.h
@@ -0,0 +1,20 @@
+//============================================================
+//
+// sound.h - Win32 implementation of MAME sound routines
+//
+// Copyright (c) 1996-2007, Nicola Salmoria and the MAME Team.
+// Visit http://mamedev.org for licensing and usage restrictions.
+//
+//============================================================
+
+#ifndef __WIN_SOUND__
+#define __WIN_SOUND__
+
+
+//============================================================
+// PROTOTYPES
+//============================================================
+
+void winsound_init(running_machine *machine);
+
+#endif
diff --git a/src/osd/windows/strconv.c b/src/osd/windows/strconv.c
new file mode 100644
index 00000000000..4cb88dbf0df
--- /dev/null
+++ b/src/osd/windows/strconv.c
@@ -0,0 +1,108 @@
+//============================================================
+//
+// strconv.h - Win32 string conversion
+//
+// Copyright (c) 1996-2007, Nicola Salmoria and the MAME Team.
+// Visit http://mamedev.org for licensing and usage restrictions.
+//
+//============================================================
+
+// standard windows headers
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+
+// MAME headers
+#include "restrack.h"
+#include "mame.h"
+
+// MAMEOS headers
+#include "strconv.h"
+#include "unicode.h"
+
+
+//============================================================
+// astring_from_utf8
+//============================================================
+
+CHAR *astring_from_utf8(const char *utf8string)
+{
+ WCHAR *wstring;
+ int char_count;
+ CHAR *result;
+
+ // convert MAME string (UTF-8) to UTF-16
+ char_count = MultiByteToWideChar(CP_UTF8, 0, utf8string, -1, NULL, 0);
+ wstring = (WCHAR *)alloca(char_count * sizeof(*wstring));
+ MultiByteToWideChar(CP_UTF8, 0, utf8string, -1, wstring, char_count);
+
+ // convert UTF-16 to "ANSI code page" string
+ char_count = WideCharToMultiByte(CP_ACP, 0, wstring, -1, NULL, 0, NULL, NULL);
+ result = (CHAR *)malloc(char_count * sizeof(*result));
+ if (result != NULL)
+ WideCharToMultiByte(CP_ACP, 0, wstring, -1, result, char_count, NULL, NULL);
+
+ return result;
+}
+
+
+//============================================================
+// utf8_from_astring
+//============================================================
+
+char *utf8_from_astring(const CHAR *astring)
+{
+ WCHAR *wstring;
+ int char_count;
+ CHAR *result;
+
+ // convert "ANSI code page" string to UTF-16
+ char_count = MultiByteToWideChar(CP_ACP, 0, astring, -1, NULL, 0);
+ wstring = (WCHAR *)alloca(char_count * sizeof(*wstring));
+ MultiByteToWideChar(CP_ACP, 0, astring, -1, wstring, char_count);
+
+ // convert UTF-16 to MAME string (UTF-8)
+ char_count = WideCharToMultiByte(CP_UTF8, 0, wstring, -1, NULL, 0, NULL, NULL);
+ result = (CHAR *)malloc(char_count * sizeof(*result));
+ if (result != NULL)
+ WideCharToMultiByte(CP_UTF8, 0, wstring, -1, result, char_count, NULL, NULL);
+
+ return result;
+}
+
+
+//============================================================
+// wstring_from_utf8
+//============================================================
+
+WCHAR *wstring_from_utf8(const char *utf8string)
+{
+ int char_count;
+ WCHAR *result;
+
+ // convert MAME string (UTF-8) to UTF-16
+ char_count = MultiByteToWideChar(CP_UTF8, 0, utf8string, -1, NULL, 0);
+ result = (WCHAR *)malloc(char_count * sizeof(*result));
+ if (result != NULL)
+ MultiByteToWideChar(CP_UTF8, 0, utf8string, -1, result, char_count);
+
+ return result;
+}
+
+
+//============================================================
+// utf8_from_wstring
+//============================================================
+
+char *utf8_from_wstring(const WCHAR *wstring)
+{
+ int char_count;
+ char *result;
+
+ // convert UTF-16 to MAME string (UTF-8)
+ char_count = WideCharToMultiByte(CP_UTF8, 0, wstring, -1, NULL, 0, NULL, NULL);
+ result = (char *)malloc(char_count * sizeof(*result));
+ if (result != NULL)
+ WideCharToMultiByte(CP_UTF8, 0, wstring, -1, result, char_count, NULL, NULL);
+
+ return result;
+}
diff --git a/src/osd/windows/strconv.h b/src/osd/windows/strconv.h
new file mode 100644
index 00000000000..20b5a190eaa
--- /dev/null
+++ b/src/osd/windows/strconv.h
@@ -0,0 +1,37 @@
+//============================================================
+//
+// strconv.h - Win32 string conversion
+//
+// Copyright (c) 1996-2007, Nicola Salmoria and the MAME Team.
+// Visit http://mamedev.org for licensing and usage restrictions.
+//
+//============================================================
+
+#ifndef __WIN_STRCONV__
+#define __WIN_STRCONV__
+
+#include "mamecore.h"
+
+
+
+//============================================================
+// FUNCTION PROTOTYPES
+//============================================================
+
+CHAR *astring_from_utf8(const char *s) ATTR_MALLOC;
+char *utf8_from_astring(const CHAR *s) ATTR_MALLOC;
+
+WCHAR *wstring_from_utf8(const char *s) ATTR_MALLOC;
+char *utf8_from_wstring(const WCHAR *s) ATTR_MALLOC;
+
+#ifdef UNICODE
+#define tstring_from_utf8 wstring_from_utf8
+#define utf8_from_tstring utf8_from_wstring
+#else // !UNICODE
+#define tstring_from_utf8 astring_from_utf8
+#define utf8_from_tstring utf8_from_astring
+#endif // UNICODE
+
+
+
+#endif // __WIN_STRCONV__
diff --git a/src/osd/windows/vconv.c b/src/osd/windows/vconv.c
new file mode 100644
index 00000000000..2f034f2d369
--- /dev/null
+++ b/src/osd/windows/vconv.c
@@ -0,0 +1,450 @@
+//============================================================
+//
+// vconv.c - VC++ parameter conversion code
+//
+// Copyright (c) 1996-2007, Nicola Salmoria and the MAME Team.
+// Visit http://mamedev.org for licensing and usage restrictions.
+//
+//============================================================
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+
+
+
+//============================================================
+// CONSTANTS
+//============================================================
+
+#define PRINT_COMMAND_LINE 0
+
+#define VS6 0x00060000
+#define VS7 0x00070000
+#define VS71 0x0007000a
+#define VS2005 0x00080000
+
+
+
+//============================================================
+// TYPE DEFINITIONS
+//============================================================
+
+typedef struct _translation_info translation_info;
+struct _translation_info
+{
+ DWORD vc_version;
+ const char *gcc_option;
+ const char *vc_option;
+};
+
+
+
+//============================================================
+// TRANSLATION TABLES
+//============================================================
+
+static const translation_info gcc_translate[] =
+{
+ { 0, "-D*", "/D*" },
+ { 0, "-U*", "/U*" },
+ { 0, "-I*", "/I*" },
+ { 0, "-o*", "~*" },
+ { 0, "-include*", "/FI*" },
+ { 0, "-c", "/c~/Fo" },
+ { 0, "-E", "/c~/E >" },
+ { 0, "-S", "/c~/Fa" },
+ { VS7, "-O0", "/Od /GS" },
+ { 0, "-O0", "/Od" },
+ { 0, "-O1", "/O2" },
+ { 0, "-O2", "/O2" },
+ { 0, "-O3", "/O2" },
+ { 0, "-Os", "/O1" },
+ { 0, "-g", "/Zi" },
+ { VS2005, "-fno-strict-aliasing", "" }, // deprecated in VS2005
+ { 0, "-fno-strict-aliasing", "/Oa" },
+ { 0, "-fno-omit-frame-pointer", "" },
+ { 0, "-Werror", "/WX" },
+ { VS2005, "-Wall", "/Wall /W3 /wd4018 /wd4146 /wd4242 /wd4244 /wd4305 /wd4619 /wd4702 /wd4706 /wd4710 /wd4711 /wd4738 /wd4826" },
+ { VS7, "-Wall", "/Wall /W3 /wd4018 /wd4146 /wd4242 /wd4244 /wd4305 /wd4550 /wd4619 /wd4702 /wd4706 /wd4710 /wd4711 /wd4826" },
+ { 0, "-Wall", "/W0" },
+ { VS7, "-Wno-unused", "/wd4100 /wd4101 /wd4102" },
+ { 0, "-W*", "" },
+ { VS2005, "-march=*", "" }, // deprecated in VS2005
+ { 0, "-march=pentium", "/G5" },
+ { 0, "-march=athlon", "/G7" },
+ { 0, "-march=pentiumpro", "/G6" },
+ { 0, "-march=pentium4", "/G7" },
+ { 0, "-march=athlon64", "/G7" },
+ { 0, "-march=pentium3", "/G6" },
+ { VS71, "-msse2", "/arch:SSE2" },
+ { 0, "-msse2", "" },
+ { 0, "-mwindows", "" },
+ { 0, "-mno-cygwin", "" },
+ { 0, "-std=gnu89", "" },
+ { 0 }
+};
+
+static const translation_info ld_translate[] =
+{
+ { 0, "-l*", "*.lib" },
+ { 0, "-o*", "/out:*" },
+ { 0, "-Wl,-Map,*", "/map:*" },
+ { 0, "-Wl,--allow-multiple-definition", "/force:multiple" },
+ { 0, "-Wl,--warn-common", "" },
+ { 0, "-mno-cygwin", "" },
+ { 0, "-s", "" },
+ { 0, "-WO", "" },
+ { 0, "-mconsole", "/subsystem:console" },
+ { 0, "-mwindows", "/subsystem:windows" },
+ { 0, "-shared", "/dll" },
+ { 0 }
+};
+
+static const translation_info ar_translate[] =
+{
+ { 0, "-cr", "" },
+ { 0 }
+};
+
+
+static const translation_info windres_translate[] =
+{
+ { 0, "-D*", "/D*" },
+ { 0, "-U*", "/U*" },
+ { 0, "-I*", "/I*" },
+ { 0, "--include-dir*", "/I*" },
+ { 0, "-o*", "/fo*" },
+ { 0, "-O*", "" },
+ { 0, "-i*", "*" },
+ { 0 }
+};
+
+
+
+//============================================================
+// GLOBALS
+//============================================================
+
+static char command_line[32768];
+
+
+
+//============================================================
+// get_exe_version
+//============================================================
+
+static DWORD get_exe_version(const char *executable)
+{
+ char path[MAX_PATH];
+ void *version_info;
+ DWORD version_info_size, dummy;
+ void *sub_buffer;
+ UINT sub_buffer_size;
+ VS_FIXEDFILEINFO *info;
+ DWORD product_version;
+ char sub_block[2] = { '\\', '\0' };
+
+ // try to locate the executable
+ if (!SearchPath(NULL, executable, NULL, sizeof(path) / sizeof(path[0]), path, NULL))
+ {
+ fprintf(stderr, "Cannot find %s\n", executable);
+ exit(-100);
+ }
+
+ // determine the size of the version info
+ version_info_size = GetFileVersionInfoSize(path, &dummy);
+ if (version_info_size == 0)
+ {
+ switch(GetLastError())
+ {
+ case ERROR_RESOURCE_TYPE_NOT_FOUND:
+ fprintf(stderr, "\"%s\" does not contain version info; this is probably not a MSVC executable\n", path);
+ break;
+
+ default:
+ fprintf(stderr, "GetFileVersionInfoSize() failed\n");
+ break;
+ }
+ exit(-100);
+ }
+
+ // allocate the memory; using GlobalAlloc() so that we do not
+ // unintentionally uses malloc() overrides
+ version_info = GlobalAlloc(GMEM_FIXED, version_info_size);
+ if (!version_info)
+ {
+ fprintf(stderr, "Out of memory\n");
+ exit(-100);
+ }
+
+ // retrieve the version info
+ if (!GetFileVersionInfo(path, 0, version_info_size, version_info))
+ {
+ fprintf(stderr, "GetFileVersionInfo() failed\n");
+ exit(-100);
+ }
+
+ // extract the VS_FIXEDFILEINFO from the version info
+ if (!VerQueryValue(version_info, sub_block, &sub_buffer, &sub_buffer_size))
+ {
+ fprintf(stderr, "VerQueryValue() failed\n");
+ exit(-100);
+ }
+
+ info = (VS_FIXEDFILEINFO *) sub_buffer;
+ product_version = info->dwProductVersionMS;
+
+ GlobalFree(version_info);
+ return product_version;
+}
+
+
+
+//============================================================
+// build_command_line
+//============================================================
+
+static void build_command_line(int argc, char *argv[])
+{
+ const translation_info *transtable;
+ const char *executable;
+ const char *outstring = "";
+ char *dst = command_line;
+ int output_is_first = 0;
+ int param;
+ DWORD exe_version;
+
+ // if no parameters, show usage
+ if (argc < 2)
+ {
+ fprintf(stderr, "Usage:\n vconv {gcc|ar|ld} [param [...]]\n");
+ exit(0);
+ }
+
+ // first parameter determines the type
+ if (!strcmp(argv[1], "gcc"))
+ {
+ transtable = gcc_translate;
+ executable = "cl.exe";
+ dst += sprintf(dst, "cl /nologo ");
+ }
+ else if (!strcmp(argv[1], "windres"))
+ {
+ transtable = windres_translate;
+ executable = "rc.exe";
+ dst += sprintf(dst, "rc ");
+ }
+ else if (!strcmp(argv[1], "ld"))
+ {
+ transtable = ld_translate;
+ executable = "link.exe";
+ dst += sprintf(dst, "link /nologo /debug ");
+ }
+ else if (!strcmp(argv[1], "ar"))
+ {
+ transtable = ar_translate;
+ executable = "lib.exe";
+ dst += sprintf(dst, "link /lib /nologo ");
+ outstring = "/out:";
+ output_is_first = 1;
+ }
+ else
+ {
+ fprintf(stderr, "Error: unknown translation type '%s'\n", argv[1]);
+ exit(-100);
+ }
+
+ // identify the version number of the EXE
+ exe_version = get_exe_version(executable);
+
+ // special case
+ if (!strcmp(executable, "cl.exe") && (exe_version >= 0x00070000))
+ dst += sprintf(dst, "/wd4025 ");
+
+ // iterate over parameters
+ for (param = 2; param < argc; param++)
+ {
+ const char *src = argv[param];
+ int srclen = strlen(src);
+ int i;
+
+ // find a match
+ if (src[0] == '-')
+ {
+ for (i = 0; transtable[i].gcc_option; i++)
+ {
+ const char *compare = transtable[i].gcc_option;
+ const char *replace;
+ int j;
+
+ // check version number
+ if (exe_version < transtable[i].vc_version)
+ continue;
+
+ // find a match
+ for (j = 0; j < srclen; j++)
+ if (src[j] != compare[j])
+ break;
+
+ // if we hit an asterisk, we're ok
+ if (compare[j] == '*')
+ {
+ // if this is the end of the parameter, use the next one
+ if (src[j] == 0)
+ src = argv[++param];
+ else
+ src += j;
+
+ // copy the replacement up to the asterisk
+ replace = transtable[i].vc_option;
+ while (*replace && *replace != '*')
+ {
+ if (*replace == '~')
+ {
+ dst += sprintf(dst, "%s", outstring);
+ replace++;
+ }
+ else
+ *dst++ = *replace++;
+ }
+
+ // if we have an asterisk in the replacement, copy the rest of the source
+ if (*replace == '*')
+ {
+ int addquote = (strchr(src, ' ') != NULL);
+
+ if (addquote)
+ *dst++ = '"';
+ while (*src)
+ {
+ *dst++ = (*src == '/') ? '\\' : *src;
+ src++;
+ }
+ if (addquote)
+ *dst++ = '"';
+
+ // if there's stuff after the asterisk, copy that
+ replace++;
+ while (*replace)
+ *dst++ = *replace++;
+ }
+
+ // append a final space
+ *dst++ = ' ';
+ break;
+ }
+
+ // if we hit the end, we're also ok
+ else if (compare[j] == 0 && j == srclen)
+ {
+ // copy the replacement up to the tilde
+ replace = transtable[i].vc_option;
+ while (*replace && *replace != '~')
+ *dst++ = *replace++;
+
+ // if we hit a tilde, set the new output
+ if (*replace == '~')
+ outstring = replace + 1;
+
+ // append a final space
+ *dst++ = ' ';
+ break;
+ }
+
+ // else keep looking
+ }
+
+ // warn if we didn't get a match
+ if (!transtable[i].gcc_option)
+ fprintf(stderr, "Unable to match parameter '%s'\n", src);
+ }
+
+ // otherwise, assume it's a filename and copy translating slashes
+ // it can also be a Windows-specific option which is passed through unscathed
+ else
+ {
+ int dotrans = (*src != '/');
+
+ // if the output filename is implicitly first, append the out parameter
+ if (output_is_first)
+ {
+ dst += sprintf(dst, "%s", outstring);
+ output_is_first = 0;
+ }
+
+ // now copy the rest of the string
+ while (*src)
+ {
+ *dst++ = (dotrans && *src == '/') ? '\\' : *src;
+ src++;
+ }
+ *dst++ = ' ';
+ }
+ }
+
+ // trim remaining spaces and NULL terminate
+ while (dst > command_line && dst[-1] == ' ')
+ dst--;
+ *dst = 0;
+}
+
+
+
+//============================================================
+// main
+//============================================================
+
+int main(int argc, char *argv[])
+{
+ PROCESS_INFORMATION pi;
+ STARTUPINFO si;
+ DWORD exitcode;
+ int uses_redirection, in_quotes, i;
+ static const char cmd_prefix[] = "cmd.exe /c ";
+
+ // build the new command line
+ build_command_line(argc, argv);
+
+ // do we use redirection? if so, use cmd.exe
+ uses_redirection = FALSE;
+ in_quotes = FALSE;
+ for (i = 0; command_line[i]; i++)
+ {
+ if (command_line[i] == '\"')
+ in_quotes = !in_quotes;
+ if (!in_quotes && strchr("|<>", command_line[i]))
+ uses_redirection = TRUE;
+ }
+ if (uses_redirection)
+ {
+ memmove(command_line + strlen(cmd_prefix), command_line, strlen(command_line) + 1);
+ memcpy(command_line, cmd_prefix, strlen(cmd_prefix));
+ }
+
+ if (PRINT_COMMAND_LINE)
+ printf("%s\n", command_line);
+
+ // create the process information structures
+ memset(&si, 0, sizeof(si));
+ si.cb = sizeof(si);
+ memset(&pi, 0, sizeof(pi));
+
+ // create and execute the process
+ if (!CreateProcess(NULL, command_line, NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi) ||
+ pi.hProcess == INVALID_HANDLE_VALUE)
+ return -101;
+
+ // block until done and fetch the error code
+ WaitForSingleObject(pi.hProcess, INFINITE);
+ GetExitCodeProcess(pi.hProcess, &exitcode);
+
+ // clean up the handles
+ CloseHandle(pi.hProcess);
+ CloseHandle(pi.hThread);
+
+ // return the child's error code
+ return exitcode;
+}
diff --git a/src/osd/windows/verinfo.c b/src/osd/windows/verinfo.c
new file mode 100644
index 00000000000..c43a742bd5d
--- /dev/null
+++ b/src/osd/windows/verinfo.c
@@ -0,0 +1,267 @@
+//============================================================
+//
+// verinfo.c - Version resource emitter code
+//
+// Copyright (c) 1996-2007, Nicola Salmoria and the MAME Team.
+// Visit http://mamedev.org for licensing and usage restrictions.
+//
+//============================================================
+
+#include <stdio.h>
+#include <ctype.h>
+
+#include "osdcore.h"
+
+
+//============================================================
+// TYPE DEFINITIONS
+//============================================================
+
+typedef struct _version_info version_info;
+struct _version_info
+{
+ int version_major;
+ int version_minor;
+ int version_build;
+ int version_subbuild;
+ const char *version_string;
+ const char *author;
+ const char *comments;
+ const char *company_name;
+ const char *file_description;
+ const char *internal_name;
+ const char *legal_copyright;
+ const char *original_filename;
+ const char *product_name;
+};
+
+
+
+//============================================================
+// emit_version_info
+//============================================================
+
+static void emit_version_info(const version_info *v)
+{
+ printf("VS_VERSION_INFO VERSIONINFO\n");
+ printf("\tFILEVERSION %d,%d,%d,%d\n", v->version_major, v->version_minor, v->version_build, v->version_subbuild);
+ printf("\tPRODUCTVERSION %d,%d,%d,%d\n", v->version_major, v->version_minor, v->version_build, v->version_subbuild);
+ printf("\tFILEFLAGSMASK 0x3fL\n");
+#ifdef MAME_DEBUG
+ if (v->version_build == 0)
+ printf("\tFILEFLAGS VS_FF_DEBUG\n");
+ else
+ printf("\tFILEFLAGS VS_FF_PRERELEASE | VS_FF_DEBUG\n");
+#else
+ if (v->version_build == 0)
+ printf("\tFILEFLAGS 0x0L\n");
+ else
+ printf("\tFILEFLAGS VS_FF_PRERELEASE\n");
+#endif
+ printf("\tFILEOS VOS_NT_WINDOWS32\n");
+ printf("\tFILETYPE VFT_APP\n");
+ printf("\tFILESUBTYPE VFT2_UNKNOWN\n");
+ printf("BEGIN\n");
+ printf("\tBLOCK \"StringFileInfo\"\n");
+ printf("\tBEGIN\n");
+ printf("#ifdef UNICODE\n");
+ printf("\t\tBLOCK \"040904b0\"\n");
+ printf("#else\n");
+ printf("\t\tBLOCK \"040904E4\"\n");
+ printf("#endif\n");
+ printf("\t\tBEGIN\n");
+ if (v->author != NULL)
+ printf("\t\t\tVALUE \"Author\", \"%s\\0\"\n", v->author);
+ if (v->comments != NULL)
+ printf("\t\t\tVALUE \"Comments\", \"%s\\0\"\n", v->comments);
+ if (v->company_name != NULL)
+ printf("\t\t\tVALUE \"CompanyName\", \"%s\\0\"\n", v->company_name);
+ if (v->file_description != NULL)
+ printf("\t\t\tVALUE \"FileDescription\", \"%s\\0\"\n", v->file_description);
+ printf("\t\t\tVALUE \"FileVersion\", \"%d, %d, %d, %d\\0\"\n", v->version_major, v->version_minor, v->version_build, v->version_subbuild);
+ if (v->internal_name != NULL)
+ printf("\t\t\tVALUE \"InternalName\", \"%s\\0\"\n", v->internal_name);
+ if (v->legal_copyright != NULL)
+ printf("\t\t\tVALUE \"LegalCopyright\", \"%s\\0\"\n", v->legal_copyright);
+ if (v->original_filename != NULL)
+ printf("\t\t\tVALUE \"OriginalFilename\", \"%s\\0\"\n", v->original_filename);
+ if (v->product_name != NULL)
+ printf("\t\t\tVALUE \"ProductName\", \"%s\\0\"\n", v->product_name);
+ printf("\t\t\tVALUE \"ProductVersion\", \"%s\\0\"\n", v->version_string);
+ printf("\t\tEND\n");
+ printf("\tEND\n");
+ printf("\tBLOCK \"VarFileInfo\"\n");
+ printf("\tBEGIN\n");
+ printf("#ifdef UNICODE\n");
+ printf("\t\tVALUE \"Translation\", 0x409, 1200\n");
+ printf("#else\n");
+ printf("\t\tVALUE \"Translation\", 0x409, 1252\n");
+ printf("#endif\n");
+ printf("\tEND\n");
+ printf("END\n");
+}
+
+
+
+//============================================================
+// parse_version_digit
+//============================================================
+
+static int parse_version_digit(const char *str, int *position)
+{
+ int value = 0;
+
+ while (str[*position] != 0 && !isspace(str[*position]) && !isdigit(str[*position]))
+ (*position)++;
+
+ if (str[*position] != 0 && isdigit(str[*position]))
+ {
+ sscanf(&str[*position], "%d", &value);
+ while (isdigit(str[*position]))
+ (*position)++;
+ }
+ return value;
+}
+
+
+
+//============================================================
+// parse_version
+//============================================================
+
+static int parse_version(char *str, int *version_major, int *version_minor, int *version_micro, int *year, const char **version_string)
+{
+ char *copyright;
+ char *version;
+ int position = 0;
+
+ // find the version string
+ version = strstr(str, "build_version");
+ if (version != NULL)
+ version = strchr(version, '"');
+ if (version == NULL)
+ {
+ fprintf(stderr, "Unable to find build_version string\n");
+ return 1;
+ }
+ version++;
+ *strchr(version, ' ') = 0;
+
+ *version_string = version;
+ *version_major = parse_version_digit(version, &position);
+ *version_minor = parse_version_digit(version, &position);
+ *version_micro = parse_version_digit(version, &position);
+
+ // find the copyright string
+ copyright = strstr(str, "(c)");
+ if (copyright != NULL)
+ copyright = strchr(copyright, '-');
+ if (copyright == NULL)
+ {
+ fprintf(stderr, "Unable to find copyright string\n");
+ return 1;
+ }
+ copyright++;
+
+ *year = 0;
+ sscanf(copyright, "%d", year);
+ return 0;
+}
+
+
+
+//============================================================
+// main
+//============================================================
+
+int main(int argc, char *argv[])
+{
+ version_info v;
+ int begin_year, current_year;
+ char legal_copyright[512];
+ char *buffer;
+ size_t size;
+ FILE *f;
+
+ memset(&v, 0, sizeof(v));
+
+ // validate parameters
+ if (argc < 2)
+ {
+ printf("Usage: %s <filename>\n", argv[0]);
+ return 0;
+ }
+
+ // open the file
+ f = fopen(argv[1], "rb");
+ if (f == NULL)
+ {
+ fprintf(stderr, "Error opening file %s\n", argv[1]);
+ return 1;
+ }
+
+ // get the file size
+ fseek(f, 0, SEEK_END);
+ size = ftell(f);
+ fseek(f, 0, SEEK_SET);
+
+ // allocate a buffer
+ buffer = malloc(size + 1);
+ if (buffer == NULL)
+ {
+ fclose(f);
+ fprintf(stderr, "Error allocating %d bytes\n", size + 1);
+ return 1;
+ }
+
+ // read the file contents and NULL-terminate
+ fread(buffer, 1, size, f);
+ fclose(f);
+ buffer[size] = 0;
+
+ // parse out version string
+ if (parse_version(buffer, &v.version_major, &v.version_minor, &v.version_build, &current_year, &v.version_string))
+ return 1;
+
+#ifdef MESS
+ // MESS
+ v.author = "MESS Team";
+ v.comments = "Multi Emulation Super System";
+ v.company_name = "MESS Team";
+ v.file_description = "Multi Emulation Super System";
+ v.internal_name = "MESS";
+ v.original_filename = "MESS";
+ v.product_name = "MESS";
+ begin_year = 1998;
+#elif defined(WINUI)
+ // MAMEUI
+ v.author = "Christopher Kirmse and the MAMEUI team";
+ v.comments = "Multiple Arcade Machine Emulator with GUI";
+ v.company_name = "MAME Team";
+ v.file_description = "Multiple Arcade Machine Emulator with GUI";
+ v.internal_name = "MAMEUI";
+ v.original_filename = "MAMEUI";
+ v.product_name = "MAMEUI";
+ begin_year = 1997;
+#else
+ // MAME
+ v.author = "Nicola Salmoria and the MAME Team";
+ v.comments = "Multiple Arcade Machine Emulator";
+ v.company_name = "MAME Team";
+ v.file_description = "Multiple Arcade Machine Emulator";
+ v.internal_name = "MAME";
+ v.original_filename = "MAME";
+ v.product_name = "MAME";
+ begin_year = 1996;
+#endif
+
+ // build legal_copyright string
+ v.legal_copyright = legal_copyright;
+ snprintf(legal_copyright, ARRAY_LENGTH(legal_copyright), "Copyright %d-%d Nicola Salmoria and the MAME team", begin_year, current_year);
+
+ // emit the info
+ emit_version_info(&v);
+
+ free(buffer);
+ return 0;
+}
diff --git a/src/osd/windows/video.c b/src/osd/windows/video.c
new file mode 100644
index 00000000000..694b9b5a345
--- /dev/null
+++ b/src/osd/windows/video.c
@@ -0,0 +1,524 @@
+//============================================================
+//
+// video.c - Win32 video handling
+//
+// Copyright (c) 1996-2007, Nicola Salmoria and the MAME Team.
+// Visit http://mamedev.org for licensing and usage restrictions.
+//
+//============================================================
+
+// needed for multimonitor
+#define _WIN32_WINNT 0x501
+
+// standard windows headers
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+
+// standard C headers
+#include <math.h>
+
+// Windows 95/NT4 multimonitor stubs
+#ifdef WIN95_MULTIMON
+#include "multidef.h"
+#endif
+
+// MAME headers
+#include "osdepend.h"
+#include "driver.h"
+#include "profiler.h"
+#include "video/vector.h"
+#include "render.h"
+#include "rendutil.h"
+#include "ui.h"
+
+// MAMEOS headers
+#include "winmain.h"
+#include "video.h"
+#include "window.h"
+#include "input.h"
+#include "debugwin.h"
+#include "strconv.h"
+#include "config.h"
+
+#ifdef MESS
+#include "menu.h"
+#endif
+
+
+//============================================================
+// GLOBAL VARIABLES
+//============================================================
+
+win_video_config video_config;
+
+
+
+//============================================================
+// LOCAL VARIABLES
+//============================================================
+
+// monitor info
+win_monitor_info *win_monitor_list;
+static win_monitor_info *primary_monitor;
+
+static mame_bitmap *effect_bitmap;
+
+
+
+//============================================================
+// PROTOTYPES
+//============================================================
+
+static void video_exit(running_machine *machine);
+static void init_monitors(void);
+static BOOL CALLBACK monitor_enum_callback(HMONITOR handle, HDC dc, LPRECT rect, LPARAM data);
+static win_monitor_info *pick_monitor(int index);
+
+static void check_osd_inputs(void);
+
+static void extract_video_config(void);
+static void load_effect_overlay(const char *filename);
+static float get_aspect(const char *name, int report_error);
+static void get_resolution(const char *name, win_window_config *config, int report_error);
+
+
+
+//============================================================
+// winvideo_init
+//============================================================
+
+void winvideo_init(running_machine *machine)
+{
+ int index;
+
+ // ensure we get called on the way out
+ add_exit_callback(machine, video_exit);
+
+ // extract data from the options
+ extract_video_config();
+
+ // set up monitors first
+ init_monitors();
+
+ // initialize the window system so we can make windows
+ winwindow_init(machine);
+
+ // create the windows
+ for (index = 0; index < video_config.numscreens; index++)
+ winwindow_video_window_create(index, pick_monitor(index), &video_config.window[index]);
+ if (video_config.mode != VIDEO_MODE_NONE)
+ SetForegroundWindow(win_window_list->hwnd);
+
+ // possibly create the debug window, but don't show it yet
+#ifdef MAME_DEBUG
+ if (options_get_bool(mame_options(), OPTION_DEBUG))
+ debugwin_init_windows();
+#endif
+}
+
+
+//============================================================
+// video_exit
+//============================================================
+
+static void video_exit(running_machine *machine)
+{
+ // free the overlay effect
+ if (effect_bitmap != NULL)
+ bitmap_free(effect_bitmap);
+ effect_bitmap = NULL;
+
+ // possibly kill the debug window
+#ifdef MAME_DEBUG
+ if (options_get_bool(mame_options(), OPTION_DEBUG))
+ debugwin_destroy_windows();
+#endif
+
+ // free all of our monitor information
+ while (win_monitor_list != NULL)
+ {
+ win_monitor_info *temp = win_monitor_list;
+ win_monitor_list = temp->next;
+ free(temp);
+ }
+}
+
+
+
+//============================================================
+// winvideo_monitor_refresh
+//============================================================
+
+void winvideo_monitor_refresh(win_monitor_info *monitor)
+{
+ HRESULT result;
+
+ // fetch the latest info about the monitor
+ monitor->info.cbSize = sizeof(monitor->info);
+ result = GetMonitorInfo(monitor->handle, (LPMONITORINFO)&monitor->info);
+ assert(result);
+}
+
+
+
+//============================================================
+// winvideo_monitor_get_aspect
+//============================================================
+
+float winvideo_monitor_get_aspect(win_monitor_info *monitor)
+{
+ // refresh the monitor information and compute the aspect
+ if (video_config.keepaspect)
+ {
+ int width, height;
+ winvideo_monitor_refresh(monitor);
+ width = rect_width(&monitor->info.rcMonitor);
+ height = rect_height(&monitor->info.rcMonitor);
+ return monitor->aspect / ((float)width / (float)height);
+ }
+ return 0.0f;
+}
+
+
+
+//============================================================
+// winvideo_monitor_from_handle
+//============================================================
+
+win_monitor_info *winvideo_monitor_from_handle(HMONITOR hmonitor)
+{
+ win_monitor_info *monitor;
+
+ // find the matching monitor
+ for (monitor = win_monitor_list; monitor != NULL; monitor = monitor->next)
+ if (monitor->handle == hmonitor)
+ return monitor;
+ return NULL;
+}
+
+
+
+//============================================================
+// osd_update
+//============================================================
+
+void osd_update(int skip_redraw)
+{
+ win_window_info *window;
+
+ // if we're not skipping this redraw, update all windows
+ if (!skip_redraw)
+ for (window = win_window_list; window != NULL; window = window->next)
+ winwindow_video_window_update(window);
+
+ // poll the joystick values here
+ winwindow_process_events(TRUE);
+ wininput_poll();
+ check_osd_inputs();
+}
+
+
+
+//============================================================
+// init_monitors
+//============================================================
+
+static void init_monitors(void)
+{
+ win_monitor_info **tailptr;
+
+ // make a list of monitors
+ win_monitor_list = NULL;
+ tailptr = &win_monitor_list;
+ EnumDisplayMonitors(NULL, NULL, monitor_enum_callback, (LPARAM)&tailptr);
+
+ // if we're verbose, print the list of monitors
+ {
+ win_monitor_info *monitor;
+ for (monitor = win_monitor_list; monitor != NULL; monitor = monitor->next)
+ {
+ char *utf8_device = utf8_from_tstring(monitor->info.szDevice);
+ if (utf8_device != NULL)
+ {
+ mame_printf_verbose("Video: Monitor %p = \"%s\" %s\n", monitor->handle, utf8_device, (monitor == primary_monitor) ? "(primary)" : "");
+ free(utf8_device);
+ }
+ }
+ }
+}
+
+
+
+//============================================================
+// monitor_enum_callback
+//============================================================
+
+static BOOL CALLBACK monitor_enum_callback(HMONITOR handle, HDC dc, LPRECT rect, LPARAM data)
+{
+ win_monitor_info ***tailptr = (win_monitor_info ***)data;
+ win_monitor_info *monitor;
+ MONITORINFOEX info;
+ BOOL result;
+
+ // get the monitor info
+ info.cbSize = sizeof(info);
+ result = GetMonitorInfo(handle, (LPMONITORINFO)&info);
+ assert(result);
+
+ // allocate a new monitor info
+ monitor = malloc_or_die(sizeof(*monitor));
+ memset(monitor, 0, sizeof(*monitor));
+
+ // copy in the data
+ monitor->handle = handle;
+ monitor->info = info;
+
+ // guess the aspect ratio assuming square pixels
+ monitor->aspect = (float)(info.rcMonitor.right - info.rcMonitor.left) / (float)(info.rcMonitor.bottom - info.rcMonitor.top);
+
+ // save the primary monitor handle
+ if (monitor->info.dwFlags & MONITORINFOF_PRIMARY)
+ primary_monitor = monitor;
+
+ // hook us into the list
+ **tailptr = monitor;
+ *tailptr = &monitor->next;
+
+ // enumerate all the available monitors so to list their names in verbose mode
+ return TRUE;
+}
+
+
+
+//============================================================
+// pick_monitor
+//============================================================
+
+static win_monitor_info *pick_monitor(int index)
+{
+ const char *scrname, *scrname2;
+ win_monitor_info *monitor;
+ int moncount = 0;
+ char option[20];
+ float aspect;
+
+ // get the screen option
+ scrname = options_get_string(mame_options(), WINOPTION_SCREEN);
+ sprintf(option, "screen%d", index);
+ scrname2 = options_get_string(mame_options(), option);
+
+ // decide which one we want to use
+ if (strcmp(scrname2, "auto") != 0)
+ scrname = scrname2;
+
+ // get the aspect ratio
+ sprintf(option, "aspect%d", index);
+ aspect = get_aspect(option, TRUE);
+
+ // look for a match in the name first
+ if (scrname[0] != 0)
+ for (monitor = win_monitor_list; monitor != NULL; monitor = monitor->next)
+ {
+ char *utf8_device;
+ int rc = 1;
+
+ moncount++;
+
+ utf8_device = utf8_from_tstring(monitor->info.szDevice);
+ if (utf8_device != NULL)
+ {
+ rc = strcmp(scrname, utf8_device);
+ free(utf8_device);
+ }
+ if (rc == 0)
+ goto finishit;
+ }
+
+ // didn't find it; alternate monitors until we hit the jackpot
+ index %= moncount;
+ for (monitor = win_monitor_list; monitor != NULL; monitor = monitor->next)
+ if (index-- == 0)
+ goto finishit;
+
+ // return the primary just in case all else fails
+ monitor = primary_monitor;
+
+finishit:
+ if (aspect != 0)
+ monitor->aspect = aspect;
+ return monitor;
+}
+
+
+
+//============================================================
+// check_osd_inputs
+//============================================================
+
+static void check_osd_inputs(void)
+{
+ // check for toggling fullscreen mode
+ if (input_ui_pressed(IPT_OSD_1))
+ winwindow_toggle_full_screen();
+
+#ifdef MESS
+ // check for toggling menu bar
+ if (input_ui_pressed(IPT_OSD_2))
+ win_toggle_menubar();
+#endif
+}
+
+
+
+//============================================================
+// extract_video_config
+//============================================================
+
+static void extract_video_config(void)
+{
+ const char *stemp;
+
+ // global options: extract the data
+ video_config.windowed = options_get_bool(mame_options(), WINOPTION_WINDOW);
+ video_config.prescale = options_get_int(mame_options(), WINOPTION_PRESCALE);
+ video_config.keepaspect = options_get_bool(mame_options(), WINOPTION_KEEPASPECT);
+ video_config.numscreens = options_get_int(mame_options(), WINOPTION_NUMSCREENS);
+#ifdef MAME_DEBUG
+ // if we are in debug mode, never go full screen
+ if (options_get_bool(mame_options(), OPTION_DEBUG))
+ video_config.windowed = TRUE;
+#endif
+ stemp = options_get_string(mame_options(), WINOPTION_EFFECT);
+ if (strcmp(stemp, "none") != 0)
+ load_effect_overlay(stemp);
+
+ // per-window options: extract the data
+ get_resolution(WINOPTION_RESOLUTION0, &video_config.window[0], TRUE);
+ get_resolution(WINOPTION_RESOLUTION1, &video_config.window[1], TRUE);
+ get_resolution(WINOPTION_RESOLUTION2, &video_config.window[2], TRUE);
+ get_resolution(WINOPTION_RESOLUTION3, &video_config.window[3], TRUE);
+
+ // video options: extract the data
+ stemp = options_get_string(mame_options(), WINOPTION_VIDEO);
+ if (strcmp(stemp, "d3d") == 0)
+ video_config.mode = VIDEO_MODE_D3D;
+ else if (strcmp(stemp, "ddraw") == 0)
+ video_config.mode = VIDEO_MODE_DDRAW;
+ else if (strcmp(stemp, "gdi") == 0)
+ video_config.mode = VIDEO_MODE_GDI;
+ else if (strcmp(stemp, "none") == 0)
+ {
+ video_config.mode = VIDEO_MODE_NONE;
+ if (options_get_int(mame_options(), OPTION_SECONDS_TO_RUN) == 0)
+ mame_printf_warning("Warning: -video none doesn't make much sense without -seconds_to_run\n");
+ }
+ else
+ {
+ mame_printf_warning("Invalid video value %s; reverting to gdi\n", stemp);
+ video_config.mode = VIDEO_MODE_GDI;
+ }
+ video_config.waitvsync = options_get_bool(mame_options(), WINOPTION_WAITVSYNC);
+ video_config.syncrefresh = options_get_bool(mame_options(), WINOPTION_SYNCREFRESH);
+ video_config.triplebuf = options_get_bool(mame_options(), WINOPTION_TRIPLEBUFFER);
+ video_config.switchres = options_get_bool(mame_options(), WINOPTION_SWITCHRES);
+
+ // ddraw options: extract the data
+ video_config.hwstretch = options_get_bool(mame_options(), WINOPTION_HWSTRETCH);
+
+ // d3d options: extract the data
+ video_config.filter = options_get_bool(mame_options(), WINOPTION_FILTER);
+ if (video_config.prescale == 0)
+ video_config.prescale = 1;
+
+ // misc options: sanity check values
+
+ // per-window options: sanity check values
+
+ // d3d options: sanity check values
+ options_get_int(mame_options(), WINOPTION_D3DVERSION);
+
+ options_get_float(mame_options(), WINOPTION_FULLSCREENBRIGHTNESS);
+ options_get_float(mame_options(), WINOPTION_FULLLSCREENCONTRAST);
+ options_get_float(mame_options(), WINOPTION_FULLSCREENGAMMA);
+}
+
+
+
+//============================================================
+// load_effect_overlay
+//============================================================
+
+static void load_effect_overlay(const char *filename)
+{
+ char *tempstr = malloc_or_die(strlen(filename) + 5);
+ int scrnum;
+ char *dest;
+
+ // append a .PNG extension
+ strcpy(tempstr, filename);
+ dest = strrchr(tempstr, '.');
+ if (dest == NULL)
+ dest = &tempstr[strlen(tempstr)];
+ strcpy(dest, ".png");
+
+ // load the file
+ effect_bitmap = render_load_png(NULL, tempstr, NULL, NULL);
+ if (effect_bitmap == NULL)
+ {
+ mame_printf_error("Unable to load PNG file '%s'\n", tempstr);
+ free(tempstr);
+ return;
+ }
+
+ // set the overlay on all screens
+ for (scrnum = 0; scrnum < MAX_SCREENS; scrnum++)
+ if (Machine->drv->screen[scrnum].tag != NULL)
+ render_container_set_overlay(render_container_get_screen(scrnum), effect_bitmap);
+
+ free(tempstr);
+}
+
+
+
+//============================================================
+// get_aspect
+//============================================================
+
+static float get_aspect(const char *name, int report_error)
+{
+ const char *defdata = options_get_string(mame_options(), WINOPTION_ASPECT);
+ const char *data = options_get_string(mame_options(), name);
+ int num = 0, den = 1;
+
+ if (strcmp(data, "auto") == 0)
+ {
+ if (strcmp(defdata, "auto") == 0)
+ return 0;
+ data = defdata;
+ }
+ if (sscanf(data, "%d:%d", &num, &den) != 2 && report_error)
+ mame_printf_error("Illegal aspect ratio value for %s = %s\n", name, data);
+ return (float)num / (float)den;
+}
+
+
+
+//============================================================
+// get_resolution
+//============================================================
+
+static void get_resolution(const char *name, win_window_config *config, int report_error)
+{
+ const char *defdata = options_get_string(mame_options(), WINOPTION_RESOLUTION);
+ const char *data = options_get_string(mame_options(), name);
+
+ config->width = config->height = config->refresh = 0;
+ if (strcmp(data, "auto") == 0)
+ {
+ if (strcmp(defdata, "auto") == 0)
+ return;
+ data = defdata;
+ }
+ if (sscanf(data, "%dx%d@%d", &config->width, &config->height, &config->refresh) < 2 && report_error)
+ mame_printf_error("Illegal resolution value for %s = %s\n", name, data);
+}
diff --git a/src/osd/windows/video.h b/src/osd/windows/video.h
new file mode 100644
index 00000000000..a3506cdc089
--- /dev/null
+++ b/src/osd/windows/video.h
@@ -0,0 +1,100 @@
+//============================================================
+//
+// video.h - Win32 implementation of MAME video routines
+//
+// Copyright (c) 1996-2007, Nicola Salmoria and the MAME Team.
+// Visit http://mamedev.org for licensing and usage restrictions.
+//
+//============================================================
+
+#ifndef __WIN_VIDEO__
+#define __WIN_VIDEO__
+
+
+//============================================================
+// CONSTANTS
+//============================================================
+
+#define MAX_WINDOWS 4
+
+#define VIDEO_MODE_NONE 0
+#define VIDEO_MODE_GDI 1
+#define VIDEO_MODE_DDRAW 2
+#define VIDEO_MODE_D3D 3
+
+
+
+//============================================================
+// TYPE DEFINITIONS
+//============================================================
+
+typedef struct _win_monitor_info win_monitor_info;
+struct _win_monitor_info
+{
+ win_monitor_info * next; // pointer to next monitor in list
+ HMONITOR handle; // handle to the monitor
+ MONITORINFOEX info; // most recently retrieved info
+ float aspect; // computed/configured aspect ratio of the physical device
+ int reqwidth; // requested width for this monitor
+ int reqheight; // requested height for this monitor
+};
+
+
+typedef struct _win_window_config win_window_config;
+struct _win_window_config
+{
+ float aspect; // decoded aspect ratio
+ int width; // decoded width
+ int height; // decoded height
+ int refresh; // decoded refresh
+};
+
+
+typedef struct _win_video_config win_video_config;
+struct _win_video_config
+{
+ // global configuration
+ int windowed; // start windowed?
+ int prescale; // prescale factor
+ int keepaspect; // keep aspect ratio
+ int numscreens; // number of screens
+ int layerconfig; // default configuration of layers
+
+ // per-window configuration
+ win_window_config window[MAX_WINDOWS]; // configuration data per-window
+
+ // hardware options
+ int mode; // output mode
+ int waitvsync; // spin until vsync
+ int syncrefresh; // sync only to refresh rate
+ int triplebuf; // triple buffer
+ int switchres; // switch resolutions
+
+ // ddraw options
+ int hwstretch; // stretch using the hardware
+
+ // d3d options
+ int filter; // enable filtering
+};
+
+
+
+//============================================================
+// GLOBAL VARIABLES
+//============================================================
+
+extern win_monitor_info *win_monitor_list;
+extern win_video_config video_config;
+
+
+//============================================================
+// PROTOTYPES
+//============================================================
+
+void winvideo_init(running_machine *machine);
+
+void winvideo_monitor_refresh(win_monitor_info *monitor);
+float winvideo_monitor_get_aspect(win_monitor_info *monitor);
+win_monitor_info *winvideo_monitor_from_handle(HMONITOR monitor);
+
+#endif
diff --git a/src/osd/windows/winalloc.c b/src/osd/windows/winalloc.c
new file mode 100644
index 00000000000..a355ebca3c8
--- /dev/null
+++ b/src/osd/windows/winalloc.c
@@ -0,0 +1,395 @@
+//============================================================
+//
+// winalloc.c - Win32 memory allocation routines
+//
+// Copyright (c) 1996-2007, Nicola Salmoria and the MAME Team.
+// Visit http://mamedev.org for licensing and usage restrictions.
+//
+//============================================================
+
+#define _WIN32_WINNT 0x0400
+
+// standard windows headers
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+
+// MAME headers
+#include "osdcore.h"
+
+// undefine any redefines we have in the prefix
+#undef malloc
+#undef calloc
+#undef realloc
+
+
+
+//============================================================
+// CONSTANTS
+//============================================================
+
+#define PAGE_SIZE 4096
+#define COOKIE_VAL 0x11335577
+
+// set this to 0 to not use guard pages
+#define USE_GUARD_PAGES 1
+
+// set this to 1 to align memory blocks to the start of a page;
+// otherwise, they are aligned to the end, thus catching array
+// overruns
+#define ALIGN_START 0
+
+// set this to 1 to record all mallocs and frees in the logfile
+#define LOG_CALLS 0
+
+
+
+//============================================================
+// TYPEDEFS
+//============================================================
+
+typedef struct _memory_entry memory_entry;
+struct _memory_entry
+{
+ memory_entry * next;
+ memory_entry * prev;
+ size_t size;
+ void * base;
+ const char * file;
+ int line;
+ int id;
+};
+
+
+
+//============================================================
+// GLOBAL VARIABLES
+//============================================================
+
+int winalloc_in_main_code = FALSE;
+
+
+
+//============================================================
+// LOCAL VARIABLES
+//============================================================
+
+static memory_entry *alloc_list;
+static memory_entry *free_list;
+static int current_id;
+
+static CRITICAL_SECTION memory_lock;
+static UINT8 memory_lock_initialized = FALSE;
+
+
+
+//============================================================
+// INLINES
+//============================================================
+
+INLINE void memory_lock_acquire(void)
+{
+ if (!memory_lock_initialized)
+ {
+ memory_lock_initialized = TRUE;
+ InitializeCriticalSection(&memory_lock);
+ }
+ EnterCriticalSection(&memory_lock);
+}
+
+
+INLINE void memory_lock_release(void)
+{
+ LeaveCriticalSection(&memory_lock);
+}
+
+
+INLINE memory_entry *allocate_entry(void)
+{
+ memory_entry *entry;
+
+ memory_lock_acquire();
+
+ // if we're out of entries, allocate some more
+ if (free_list == NULL)
+ {
+ int entries_per_page = PAGE_SIZE / sizeof(memory_entry);
+
+ // allocate a new pages' worth of entry
+ entry = (memory_entry *)VirtualAlloc(NULL, PAGE_SIZE, MEM_COMMIT, PAGE_READWRITE);
+ if (entry == NULL)
+ {
+ fprintf(stderr, "Out of memory for malloc tracking!\n");
+ exit(1);
+ }
+
+ // add all the entries to the list
+ while (entries_per_page--)
+ {
+ entry->next = free_list;
+ free_list = entry;
+ entry++;
+ }
+ }
+
+ // grab a free list entry
+ entry = free_list;
+ free_list = free_list->next;
+
+ // add ourselves to the alloc list
+ entry->next = alloc_list;
+ if (entry->next)
+ entry->next->prev = entry;
+ entry->prev = NULL;
+ alloc_list = entry;
+
+ memory_lock_release();
+
+ return entry;
+}
+
+
+INLINE memory_entry *find_entry(void *pointer)
+{
+ memory_entry *entry;
+
+ // scan the list looking for a matching base
+ if (pointer)
+ {
+ memory_lock_acquire();
+
+ for (entry = alloc_list; entry; entry = entry->next)
+ if (entry->base == pointer)
+ break;
+
+ memory_lock_release();
+ return entry;
+ }
+ return NULL;
+}
+
+
+INLINE void free_entry(memory_entry *entry)
+{
+ memory_lock_acquire();
+
+ // remove ourselves from the alloc list
+ if (entry->prev)
+ entry->prev->next = entry->next;
+ else
+ alloc_list = entry->next;
+ if (entry->next)
+ entry->next->prev = entry->prev;
+
+ // add ourself to the free list
+ entry->next = free_list;
+ free_list = entry;
+
+ memory_lock_release();
+}
+
+
+
+//============================================================
+// IMPLEMENTATION
+//============================================================
+
+void *malloc_file_line(size_t size, const char *file, int line)
+{
+ UINT8 *page_base, *block_base;
+ size_t rounded_size;
+ memory_entry *entry;
+
+ if (USE_GUARD_PAGES)
+ {
+ // round the size up to a page boundary
+ rounded_size = ((size + PAGE_SIZE - 1) / PAGE_SIZE) * PAGE_SIZE;
+
+ // reserve that much memory, plus two guard pages
+ page_base = VirtualAlloc(NULL, rounded_size + 2 * PAGE_SIZE, MEM_RESERVE, PAGE_NOACCESS);
+ if (page_base == NULL)
+ return NULL;
+
+ // now allow access to everything but the first and last pages
+ page_base = VirtualAlloc(page_base + PAGE_SIZE, rounded_size, MEM_COMMIT, PAGE_READWRITE);
+ if (page_base == NULL)
+ return NULL;
+
+ // work backwards from the page base to get to the block base
+ if (ALIGN_START)
+ block_base = page_base;
+ else
+ block_base = page_base + rounded_size - size;
+ }
+ else
+ {
+ block_base = (UINT8 *)GlobalAlloc(GMEM_FIXED, size);
+ }
+
+ // fill in the entry
+ entry = allocate_entry();
+ entry->size = size;
+ entry->base = block_base;
+ entry->file = file;
+ entry->line = line;
+ entry->id = current_id++;
+
+//if (entry->size == 72 && IsDebuggerPresent()) DebugBreak();
+
+#if LOG_CALLS
+ // logging
+ if (entry->file)
+ logerror("malloc #%06d size = %d (%s:%d)\n", entry->size, entry->file, entry->line);
+ else
+ logerror("malloc #%06d size = %d\n", entry->id, entry->size);
+#endif
+
+ return block_base;
+}
+
+
+void *CLIB_DECL malloc(size_t size)
+{
+ return malloc_file_line(size, NULL, 0);
+}
+
+
+void *calloc_file_line(size_t size, size_t count, const char *file, int line)
+{
+ // first allocate the memory
+ void *memory = malloc_file_line(size * count, file, line);
+ if (memory == NULL)
+ return NULL;
+
+ // then memset it
+ memset(memory, 0, size * count);
+ return memory;
+}
+
+
+void *CLIB_DECL calloc(size_t size, size_t count)
+{
+ return calloc_file_line(size, count, NULL, 0);
+}
+
+
+// this function is called by beginthreadex
+void *CLIB_DECL _calloc_crt(size_t size, size_t count)
+{
+ return calloc_file_line(size, count, NULL, 0);
+}
+
+
+void *realloc_file_line(void *memory, size_t size, const char *file, int line)
+{
+ void *newmemory = NULL;
+
+ // if size is non-zero, we need to reallocate memory
+ if (size != 0)
+ {
+ // allocate space for the new amount
+ newmemory = malloc_file_line(size, file, line);
+ if (newmemory == NULL)
+ return NULL;
+
+ // if we have an old pointer, copy it
+ if (memory != NULL)
+ {
+ memory_entry *entry = find_entry(memory);
+ if (entry == NULL)
+ {
+ if (winalloc_in_main_code)
+ {
+ fprintf(stderr, "Error: realloc a non-existant block (%s:%d)\n", file, line);
+ osd_break_into_debugger("Error: realloc a non-existant block\n");
+ }
+ }
+ else
+ memcpy(newmemory, memory, (size < entry->size) ? size : entry->size);
+ }
+ }
+
+ // if we have an original pointer, free it
+ if (memory != NULL)
+ free(memory);
+
+ return newmemory;
+}
+
+
+void *CLIB_DECL realloc(void *memory, size_t size)
+{
+ return realloc_file_line(memory, size, NULL, 0);
+}
+
+
+void CLIB_DECL free(void *memory)
+{
+ memory_entry *entry;
+
+ // allow NULL frees
+ if (memory == NULL)
+ return;
+
+ // error if no entry found
+ entry = find_entry(memory);
+ if (entry == NULL)
+ {
+ if (winalloc_in_main_code)
+ {
+ fprintf(stderr, "Error: free a non-existant block\n");
+ osd_break_into_debugger("Error: free a non-existant block");
+ }
+ return;
+ }
+ free_entry(entry);
+
+ // free the memory
+ if (USE_GUARD_PAGES)
+ VirtualFree((UINT8 *)memory - ((size_t)memory & (PAGE_SIZE-1)) - PAGE_SIZE, 0, MEM_RELEASE);
+ else
+ GlobalFree(memory);
+
+#if LOG_CALLS
+ logerror("free #%06d size = %d\n", entry->id, entry->size);
+#endif
+}
+
+
+size_t CLIB_DECL _msize(void *memory)
+{
+ memory_entry *entry = find_entry(memory);
+ if (entry == NULL)
+ {
+ if (winalloc_in_main_code)
+ {
+ fprintf(stderr, "Error: msize a non-existant block\n");
+ osd_break_into_debugger("Error: msize a non-existant block");
+ }
+ return 0;
+ }
+ return entry->size;
+}
+
+
+void check_unfreed_mem(void)
+{
+ memory_entry *entry;
+ int total = 0;
+
+ memory_lock_acquire();
+
+ // check for leaked memory
+ for (entry = alloc_list; entry; entry = entry->next)
+ if (entry->file != NULL)
+ {
+ if (total == 0)
+ fprintf(stderr, "--- memory leak warning ---\n");
+ total += entry->size;
+ fprintf(stderr, "allocation #%06d, %d bytes (%s:%d)\n", entry->id, entry->size, entry->file, entry->line);
+ }
+
+ memory_lock_release();
+
+ if (total > 0)
+ fprintf(stderr, "a total of %d bytes were not free()'d\n", total);
+}
diff --git a/src/osd/windows/windir.c b/src/osd/windows/windir.c
new file mode 100644
index 00000000000..0bfaca04202
--- /dev/null
+++ b/src/osd/windows/windir.c
@@ -0,0 +1,155 @@
+/***************************************************************************
+
+ windir.c - Win32 OSD core directory access functions
+
+ Copyright (c) 1996-2007, Nicola Salmoria and the MAME Team.
+ Visit http://mamedev.org for licensing and usage restrictions.
+
+***************************************************************************/
+
+// standard windows headers
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#include <shlwapi.h>
+#include <tchar.h>
+
+// standard C headers
+#include <stdio.h>
+#include <ctype.h>
+
+// MAME headers
+#include "osdcore.h"
+
+// MAMEOS headers
+#include "strconv.h"
+#include "winutil.h"
+
+
+//============================================================
+// TYPE DEFINITIONS
+//============================================================
+
+struct _osd_directory
+{
+ HANDLE find; // handle to the finder
+ int is_first; // TRUE if this is the first entry
+ osd_directory_entry entry; // current entry's data
+ WIN32_FIND_DATA data; // current raw data
+};
+
+
+
+//============================================================
+// osd_opendir
+//============================================================
+
+osd_directory *osd_opendir(const char *dirname)
+{
+ osd_directory *dir = NULL;
+ TCHAR *t_dirname = NULL;
+ TCHAR *dirfilter = NULL;
+ size_t dirfilter_size;
+
+ // allocate memory to hold the osd_tool_directory structure
+ dir = malloc(sizeof(*dir));
+ if (dir == NULL)
+ goto error;
+ memset(dir, 0, sizeof(*dir));
+
+ // initialize the structure
+ dir->find = INVALID_HANDLE_VALUE;
+ dir->is_first = TRUE;
+
+ // convert the path to TCHARs
+ t_dirname = tstring_from_utf8(dirname);
+ if (t_dirname == NULL)
+ goto error;
+
+ // append \*.* to the directory name
+ dirfilter_size = _tcslen(t_dirname) + 5;
+ dirfilter = (TCHAR *)malloc(dirfilter_size * sizeof(*dirfilter));
+ if (dirfilter == NULL)
+ goto error;
+ _sntprintf(dirfilter, dirfilter_size, TEXT("%s\\*.*"), t_dirname);
+
+ // attempt to find the first file
+ dir->find = FindFirstFile(dirfilter, &dir->data);
+
+error:
+ // cleanup
+ if (t_dirname != NULL)
+ free(t_dirname);
+ if (dirfilter != NULL)
+ free(dirfilter);
+ if (dir != NULL && dir->find == INVALID_HANDLE_VALUE)
+ {
+ free(dir);
+ dir = NULL;
+ }
+ return dir;
+}
+
+
+//============================================================
+// osd_readdir
+//============================================================
+
+const osd_directory_entry *osd_readdir(osd_directory *dir)
+{
+ // if we've previously allocated a name, free it now
+ if (dir->entry.name != NULL)
+ {
+ free((void *)dir->entry.name);
+ dir->entry.name = NULL;
+ }
+
+ // if this isn't the first file, do a find next
+ if (!dir->is_first)
+ {
+ if (!FindNextFile(dir->find, &dir->data))
+ return NULL;
+ }
+
+ // otherwise, just use the data we already had
+ else
+ dir->is_first = FALSE;
+
+ // extract the data
+ dir->entry.name = utf8_from_tstring(dir->data.cFileName);
+ dir->entry.type = win_attributes_to_entry_type(dir->data.dwFileAttributes);
+ dir->entry.size = dir->data.nFileSizeLow | ((UINT64) dir->data.nFileSizeHigh << 32);
+ return (dir->entry.name != NULL) ? &dir->entry : NULL;
+}
+
+
+//============================================================
+// osd_closedir
+//============================================================
+
+void osd_closedir(osd_directory *dir)
+{
+ // free any data associated
+ if (dir->entry.name != NULL)
+ free((void *)dir->entry.name);
+ if (dir->find != INVALID_HANDLE_VALUE)
+ FindClose(dir->find);
+ free(dir);
+}
+
+
+//============================================================
+// osd_is_absolute_path
+//============================================================
+
+int osd_is_absolute_path(const char *path)
+{
+ int result = FALSE;
+ TCHAR *t_path = tstring_from_utf8(path);
+ if (t_path != NULL)
+ {
+ result = !PathIsRelative(t_path);
+ free(t_path);
+ }
+ return result;
+}
+
diff --git a/src/osd/windows/window.c b/src/osd/windows/window.c
new file mode 100644
index 00000000000..7ccd3b1940e
--- /dev/null
+++ b/src/osd/windows/window.c
@@ -0,0 +1,1885 @@
+//============================================================
+//
+// window.c - Win32 window handling
+//
+// Copyright (c) 1996-2007, Nicola Salmoria and the MAME Team.
+// Visit http://mamedev.org for licensing and usage restrictions.
+//
+//============================================================
+
+#define LOG_THREADS 0
+#define LOG_TEMP_PAUSE 0
+
+// Needed for RAW Input
+#define _WIN32_WINNT 0x501
+#define WM_INPUT 0x00FF
+
+// standard windows headers
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#include <windowsx.h>
+#include <mmsystem.h>
+
+// standard C headers
+#include <math.h>
+#include <process.h>
+
+// MAME headers
+#include "osdepend.h"
+#include "driver.h"
+
+// MAMEOS headers
+#include "winmain.h"
+#include "window.h"
+#include "video.h"
+#include "input.h"
+#include "debugwin.h"
+#include "strconv.h"
+#include "config.h"
+#include "winutf8.h"
+
+#ifdef MESS
+#include "menu.h"
+#endif /* MESS */
+
+extern int drawnone_init(win_draw_callbacks *callbacks);
+extern int drawgdi_init(win_draw_callbacks *callbacks);
+extern int drawdd_init(win_draw_callbacks *callbacks);
+extern int drawd3d_init(win_draw_callbacks *callbacks);
+
+
+//============================================================
+// PARAMETERS
+//============================================================
+
+// window styles
+#define WINDOW_STYLE WS_OVERLAPPEDWINDOW
+#define WINDOW_STYLE_EX 0
+
+// debugger window styles
+#define DEBUG_WINDOW_STYLE WS_OVERLAPPED
+#define DEBUG_WINDOW_STYLE_EX 0
+
+// full screen window styles
+#define FULLSCREEN_STYLE WS_POPUP
+#define FULLSCREEN_STYLE_EX WS_EX_TOPMOST
+
+// minimum window dimension
+#define MIN_WINDOW_DIM 200
+
+// custom window messages
+#define WM_USER_FINISH_CREATE_WINDOW (WM_USER + 0)
+#define WM_USER_SELF_TERMINATE (WM_USER + 1)
+#define WM_USER_REDRAW (WM_USER + 2)
+#define WM_USER_SET_FULLSCREEN (WM_USER + 3)
+#define WM_USER_SET_MAXSIZE (WM_USER + 4)
+#define WM_USER_SET_MINSIZE (WM_USER + 5)
+#define WM_USER_UI_TEMP_PAUSE (WM_USER + 6)
+#define WM_USER_EXEC_FUNC (WM_USER + 7)
+
+
+
+//============================================================
+// GLOBAL VARIABLES
+//============================================================
+
+win_window_info *win_window_list;
+static win_window_info **last_window_ptr;
+static DWORD main_threadid;
+
+// actual physical resolution
+static int win_physical_width;
+static int win_physical_height;
+
+
+
+//============================================================
+// LOCAL VARIABLES
+//============================================================
+
+// event handling
+static DWORD last_event_check;
+
+// debugger
+static int in_background;
+
+static int ui_temp_pause;
+static int ui_temp_was_paused;
+
+static int multithreading_enabled;
+
+static HANDLE window_thread;
+static DWORD window_threadid;
+
+static DWORD last_update_time;
+
+static win_draw_callbacks draw;
+
+static HANDLE ui_pause_event;
+static HANDLE window_thread_ready_event;
+
+
+
+//============================================================
+// PROTOTYPES
+//============================================================
+
+static void winwindow_exit(running_machine *machine);
+static void winwindow_video_window_destroy(win_window_info *window);
+static void draw_video_contents(win_window_info *window, HDC dc, int update);
+
+static unsigned __stdcall thread_entry(void *param);
+static int complete_create(win_window_info *window);
+static void create_window_class(void);
+static void set_starting_view(int index, win_window_info *window, const char *view);
+
+static void constrain_to_aspect_ratio(win_window_info *window, RECT *rect, int adjustment);
+static void get_min_bounds(win_window_info *window, RECT *bounds, int constrain);
+static void get_max_bounds(win_window_info *window, RECT *bounds, int constrain);
+static void update_minmax_state(win_window_info *window);
+static void minimize_window(win_window_info *window);
+static void maximize_window(win_window_info *window);
+
+static void adjust_window_position_after_major_change(win_window_info *window);
+static void set_fullscreen(win_window_info *window, int fullscreen);
+
+
+// temporary hacks
+#if LOG_THREADS
+struct _mtlog
+{
+ osd_ticks_t timestamp;
+ const char *event;
+};
+
+static struct _mtlog mtlog[100000];
+static volatile LONG mtlogindex;
+
+void mtlog_add(const char *event)
+{
+ int index = InterlockedIncrement((LONG *) &mtlogindex) - 1;
+ if (index < ARRAY_LENGTH(mtlog))
+ {
+ mtlog[index].timestamp = osd_ticks();
+ mtlog[index].event = event;
+ }
+}
+
+static void mtlog_dump(void)
+{
+ osd_ticks_t cps = osd_ticks_per_second();
+ osd_ticks_t last = mtlog[0].timestamp * 1000000 / cps;
+ int i;
+
+ FILE *f = fopen("mt.log", "w");
+ for (i = 0; i < mtlogindex; i++)
+ {
+ osd_ticks_t curr = mtlog[i].timestamp * 1000000 / cps;
+ fprintf(f, "%20I64d %10I64d %s\n", curr, curr - last, mtlog[i].event);
+ last = curr;
+ }
+ fclose(f);
+}
+#else
+void mtlog_add(const char *event) { }
+#endif
+
+
+
+//============================================================
+// winwindow_init
+// (main thread)
+//============================================================
+
+void winwindow_init(running_machine *machine)
+{
+ size_t temp;
+
+ // determine if we are using multithreading or not
+ multithreading_enabled = options_get_bool(mame_options(), WINOPTION_MULTITHREADING);
+
+ // get the main thread ID before anything else
+ main_threadid = GetCurrentThreadId();
+
+ // ensure we get called on the way out
+ add_exit_callback(machine, winwindow_exit);
+
+ // set up window class and register it
+ create_window_class();
+
+ // create an event to signal UI pausing
+ ui_pause_event = CreateEvent(NULL, TRUE, FALSE, NULL);
+ if (!ui_pause_event)
+ fatalerror("Failed to create pause event");
+
+ // if multithreading, create a thread to run the windows
+ if (multithreading_enabled)
+ {
+ // create an event to signal when the window thread is ready
+ window_thread_ready_event = CreateEvent(NULL, TRUE, FALSE, NULL);
+ if (!window_thread_ready_event)
+ fatalerror("Failed to create window thread ready event");
+
+ // create a thread to run the windows from
+ temp = _beginthreadex(NULL, 0, thread_entry, NULL, 0, (unsigned *)&window_threadid);
+ window_thread = (HANDLE)temp;
+ if (window_thread == NULL)
+ fatalerror("Failed to create window thread");
+
+ // set the thread priority equal to the main MAME thread
+ SetThreadPriority(window_thread, GetThreadPriority(GetCurrentThread()));
+ }
+
+ // otherwise, treat the window thread as the main thread
+ else
+ {
+ window_thread = GetCurrentThread();
+ window_threadid = main_threadid;
+ }
+
+ // initialize the drawers
+ if (video_config.mode == VIDEO_MODE_D3D)
+ {
+ if (drawd3d_init(&draw))
+ video_config.mode = VIDEO_MODE_GDI;
+ }
+ if (video_config.mode == VIDEO_MODE_DDRAW)
+ {
+ if (drawdd_init(&draw))
+ video_config.mode = VIDEO_MODE_GDI;
+ }
+ if (video_config.mode == VIDEO_MODE_GDI)
+ drawgdi_init(&draw);
+ if (video_config.mode == VIDEO_MODE_NONE)
+ drawnone_init(&draw);
+
+ // set up the window list
+ last_window_ptr = &win_window_list;
+}
+
+
+
+//============================================================
+// winwindow_exit
+// (main thread)
+//============================================================
+
+static void winwindow_exit(running_machine *machine)
+{
+ assert(GetCurrentThreadId() == main_threadid);
+
+ // free all the windows
+ while (win_window_list != NULL)
+ {
+ win_window_info *temp = win_window_list;
+ win_window_list = temp->next;
+ winwindow_video_window_destroy(temp);
+ }
+
+ // kill the drawers
+ (*draw.exit)();
+
+ // if we're multithreaded, clean up the window thread
+ if (multithreading_enabled)
+ {
+ PostThreadMessage(window_threadid, WM_USER_SELF_TERMINATE, 0, 0);
+ WaitForSingleObject(window_thread, INFINITE);
+
+#if (LOG_THREADS)
+ mtlog_dump();
+#endif
+ }
+
+ // kill the UI pause event
+ if (ui_pause_event)
+ CloseHandle(ui_pause_event);
+
+ // kill the window thread ready event
+ if (window_thread_ready_event)
+ CloseHandle(window_thread_ready_event);
+
+ // if we hid the cursor during the emulation, show it
+ while (ShowCursor(TRUE) < 0) ;
+}
+
+
+
+//============================================================
+// winwindow_process_events_periodic
+// (main thread)
+//============================================================
+
+void winwindow_process_events_periodic(void)
+{
+ DWORD currticks = GetTickCount();
+
+ assert(GetCurrentThreadId() == main_threadid);
+
+ // update once every 1/8th of a second
+ if (currticks - last_event_check < 1000 / 8)
+ return;
+ winwindow_process_events(TRUE);
+}
+
+
+
+//============================================================
+// is_mame_window
+//============================================================
+
+static BOOL is_mame_window(HWND hwnd)
+{
+ win_window_info *window;
+
+ for (window = win_window_list; window != NULL; window = window->next)
+ if (window->hwnd == hwnd)
+ return TRUE;
+
+ return FALSE;
+}
+
+
+
+//============================================================
+// winwindow_process_events
+// (main thread)
+//============================================================
+
+void winwindow_process_events(int ingame)
+{
+ MSG message;
+
+ assert(GetCurrentThreadId() == main_threadid);
+
+ // if we're running, disable some parts of the debugger
+#if defined(MAME_DEBUG)
+ if (ingame)
+ debugwin_update_during_game();
+#endif
+
+ // remember the last time we did this
+ last_event_check = GetTickCount();
+
+ do
+ {
+ // if we are paused, lets wait for a message
+ if (ui_temp_pause > 0)
+ WaitMessage();
+
+ // loop over all messages in the queue
+ while (PeekMessage(&message, NULL, 0, 0, PM_REMOVE))
+ {
+ int dispatch = TRUE;
+
+ if (message.hwnd == NULL || is_mame_window(message.hwnd))
+ {
+ switch (message.message)
+ {
+ // ignore keyboard messages
+ case WM_SYSKEYUP:
+ case WM_SYSKEYDOWN:
+#ifndef MESS
+ case WM_KEYUP:
+ case WM_KEYDOWN:
+ case WM_CHAR:
+#endif
+ dispatch = FALSE;
+ break;
+
+ // forward mouse button downs to the input system
+ case WM_LBUTTONDOWN:
+ wininput_handle_mouse_button(0, TRUE, GET_X_LPARAM(message.lParam), GET_Y_LPARAM(message.lParam));
+ dispatch = FALSE;
+ break;
+
+ case WM_RBUTTONDOWN:
+ wininput_handle_mouse_button(1, TRUE, GET_X_LPARAM(message.lParam), GET_Y_LPARAM(message.lParam));
+ dispatch = FALSE;
+ break;
+
+ case WM_MBUTTONDOWN:
+ wininput_handle_mouse_button(2, TRUE, GET_X_LPARAM(message.lParam), GET_Y_LPARAM(message.lParam));
+ dispatch = FALSE;
+ break;
+
+ case WM_XBUTTONDOWN:
+ wininput_handle_mouse_button(3, TRUE, GET_X_LPARAM(message.lParam), GET_Y_LPARAM(message.lParam));
+ dispatch = FALSE;
+ break;
+
+ // forward mouse button ups to the input system
+ case WM_LBUTTONUP:
+ wininput_handle_mouse_button(0, FALSE, GET_X_LPARAM(message.lParam), GET_Y_LPARAM(message.lParam));
+ dispatch = FALSE;
+ break;
+
+ case WM_RBUTTONUP:
+ wininput_handle_mouse_button(1, FALSE, GET_X_LPARAM(message.lParam), GET_Y_LPARAM(message.lParam));
+ dispatch = FALSE;
+ break;
+
+ case WM_MBUTTONUP:
+ wininput_handle_mouse_button(2, FALSE, GET_X_LPARAM(message.lParam), GET_Y_LPARAM(message.lParam));
+ dispatch = FALSE;
+ break;
+
+ case WM_XBUTTONUP:
+ wininput_handle_mouse_button(3, FALSE, GET_X_LPARAM(message.lParam), GET_Y_LPARAM(message.lParam));
+ dispatch = FALSE;
+ break;
+ }
+ }
+
+ // dispatch if necessary
+ if (dispatch)
+ winwindow_dispatch_message(&message);
+ }
+ } while (ui_temp_pause > 0);
+
+ // update the cursor state after processing events
+ winwindow_update_cursor_state();
+}
+
+
+
+//============================================================
+// winwindow_dispatch_message
+// (main thread)
+//============================================================
+
+void winwindow_dispatch_message(MSG *message)
+{
+ assert(GetCurrentThreadId() == main_threadid);
+
+ // dispatch our special communication messages
+ switch (message->message)
+ {
+ // special case for quit
+ case WM_QUIT:
+ mame_schedule_exit(Machine);
+ break;
+
+ // temporary pause from the window thread
+ case WM_USER_UI_TEMP_PAUSE:
+ winwindow_ui_pause_from_main_thread(message->wParam);
+ break;
+
+ // execute arbitrary function
+ case WM_USER_EXEC_FUNC:
+ {
+ void (*func)(void *) = (void (*)(void *)) message->wParam;
+ void *param = (void *) message->lParam;
+ func(param);
+ }
+ break;
+
+ // everything else dispatches normally
+ default:
+ TranslateMessage(message);
+ DispatchMessage(message);
+ break;
+ }
+}
+
+
+
+//============================================================
+// winwindow_toggle_full_screen
+// (main thread)
+//============================================================
+
+void winwindow_toggle_full_screen(void)
+{
+ win_window_info *window;
+
+ assert(GetCurrentThreadId() == main_threadid);
+
+#ifdef MAME_DEBUG
+ // if we are in debug mode, never go full screen
+ if (options_get_bool(mame_options(), OPTION_DEBUG))
+ return;
+#endif
+
+ // toggle the window mode
+ video_config.windowed = !video_config.windowed;
+
+ // iterate over windows and toggle their fullscreen state
+ for (window = win_window_list; window != NULL; window = window->next)
+ SendMessage(window->hwnd, WM_USER_SET_FULLSCREEN, !video_config.windowed, 0);
+ SetForegroundWindow(win_window_list->hwnd);
+}
+
+
+
+//============================================================
+// winwindow_has_focus
+// (main or window thread)
+//============================================================
+
+BOOL winwindow_has_focus(void)
+{
+ HWND focuswnd = GetFocus();
+ win_window_info *window;
+
+ // see if one of the video windows has focus
+ for (window = win_window_list; window != NULL; window = window->next)
+ if (focuswnd == window->hwnd)
+ return TRUE;
+
+ return FALSE;
+}
+
+
+//============================================================
+// winwindow_update_cursor_state
+// (main thread)
+//============================================================
+
+void winwindow_update_cursor_state(void)
+{
+ static POINT saved_cursor_pos = { -1, -1 };
+
+ assert(GetCurrentThreadId() == main_threadid);
+
+ // if we should hide the mouse, then do it
+ // rules are:
+ // 1. we must have focus before hiding the cursor
+ // 2. we also hide the cursor in full screen mode and when the window doesn't have a menu
+ // 3. we also hide the cursor in windowed mode if we're not paused and
+ // the input system requests it
+ if (winwindow_has_focus() && ((!video_config.windowed && !win_has_menu(win_window_list)) || (!mame_is_paused(Machine) && wininput_should_hide_mouse())))
+ {
+ win_window_info *window = win_window_list;
+ RECT bounds;
+
+ // hide cursor
+ while (ShowCursor(FALSE) >= -1) ;
+ ShowCursor(TRUE);
+
+ // store the cursor position
+ GetCursorPos(&saved_cursor_pos);
+
+ // clip cursor to game video window
+ GetClientRect(window->hwnd, &bounds);
+ ClientToScreen(window->hwnd, &((POINT *)&bounds)[0]);
+ ClientToScreen(window->hwnd, &((POINT *)&bounds)[1]);
+ ClipCursor(&bounds);
+ }
+ else
+ {
+ // show cursor
+ while (ShowCursor(TRUE) < 1) ;
+ ShowCursor(FALSE);
+
+ // allow cursor to move freely
+ ClipCursor(NULL);
+ if (saved_cursor_pos.x != -1 || saved_cursor_pos.y != -1)
+ {
+ SetCursorPos(saved_cursor_pos.x, saved_cursor_pos.y);
+ saved_cursor_pos.x = saved_cursor_pos.y = -1;
+ }
+ }
+}
+
+
+
+//============================================================
+// winwindow_video_window_create
+// (main thread)
+//============================================================
+
+void winwindow_video_window_create(int index, win_monitor_info *monitor, const win_window_config *config)
+{
+ win_window_info *window, *win;
+ char option[20];
+
+ assert(GetCurrentThreadId() == main_threadid);
+
+ // allocate a new window object
+ window = malloc_or_die(sizeof(*window));
+ memset(window, 0, sizeof(*window));
+ window->maxwidth = config->width;
+ window->maxheight = config->height;
+ window->refresh = config->refresh;
+ window->monitor = monitor;
+ window->fullscreen = !video_config.windowed;
+
+ // see if we are safe for fullscreen
+ window->fullscreen_safe = TRUE;
+ for (win = win_window_list; win != NULL; win = win->next)
+ if (win->monitor == monitor)
+ window->fullscreen_safe = FALSE;
+
+ // add us to the list
+ *last_window_ptr = window;
+ last_window_ptr = &window->next;
+
+ // create a lock that we can use to skip blitting
+ window->render_lock = osd_lock_alloc();
+
+ // load the layout
+ window->target = render_target_alloc(NULL, 0);
+ if (window->target == NULL)
+ fatalerror("Error creating render target for window %d", index);
+
+ // set the specific view
+ sprintf(option, "view%d", index);
+ set_starting_view(index, window, options_get_string(mame_options(), option));
+
+ // remember the current values in case they change
+ window->targetview = render_target_get_view(window->target);
+ window->targetorient = render_target_get_orientation(window->target);
+ window->targetlayerconfig = render_target_get_layer_config(window->target);
+
+ // make the window title
+ if (video_config.numscreens == 1)
+ sprintf(window->title, APPNAME ": %s [%s]", Machine->gamedrv->description, Machine->gamedrv->name);
+ else
+ sprintf(window->title, APPNAME ": %s [%s] - Screen %d", Machine->gamedrv->description, Machine->gamedrv->name, index);
+
+ // set the initial maximized state
+ window->startmaximized = options_get_bool(mame_options(), WINOPTION_MAXIMIZE);
+
+ // finish the window creation on the window thread
+ if (multithreading_enabled)
+ {
+ // wait until the window thread is ready to respond to events
+ WaitForSingleObject(window_thread_ready_event, INFINITE);
+
+ PostThreadMessage(window_threadid, WM_USER_FINISH_CREATE_WINDOW, 0, (LPARAM)window);
+ while (window->init_state == 0)
+ Sleep(1);
+ }
+ else
+ window->init_state = complete_create(window) ? -1 : 1;
+
+ // handle error conditions
+ if (window->init_state == -1)
+ fatalerror("Unable to complete window creation");
+}
+
+
+
+//============================================================
+// winwindow_video_window_destroy
+// (main thread)
+//============================================================
+
+static void winwindow_video_window_destroy(win_window_info *window)
+{
+ win_window_info **prevptr;
+
+ assert(GetCurrentThreadId() == main_threadid);
+
+ // remove us from the list
+ for (prevptr = &win_window_list; *prevptr != NULL; prevptr = &(*prevptr)->next)
+ if (*prevptr == window)
+ {
+ *prevptr = window->next;
+ break;
+ }
+
+ // destroy the window
+ if (window->hwnd != NULL)
+ SendMessage(window->hwnd, WM_USER_SELF_TERMINATE, 0, 0);
+
+ // free the render target
+ if (window->target != NULL)
+ render_target_free(window->target);
+
+ // free the lock
+ osd_lock_free(window->render_lock);
+
+ // free the window itself
+ free(window);
+}
+
+
+
+//============================================================
+// winwindow_video_window_update
+// (main thread)
+//============================================================
+
+void winwindow_video_window_update(win_window_info *window)
+{
+ int targetview, targetorient, targetlayerconfig;
+
+ assert(GetCurrentThreadId() == main_threadid);
+
+ mtlog_add("winwindow_video_window_update: begin");
+
+ // see if the target has changed significantly in window mode
+ targetview = render_target_get_view(window->target);
+ targetorient = render_target_get_orientation(window->target);
+ targetlayerconfig = render_target_get_layer_config(window->target);
+ if (targetview != window->targetview || targetorient != window->targetorient || targetlayerconfig != window->targetlayerconfig)
+ {
+ window->targetview = targetview;
+ window->targetorient = targetorient;
+ window->targetlayerconfig = targetlayerconfig;
+
+ // in window mode, reminimize/maximize
+ if (!window->fullscreen)
+ {
+ if (window->isminimized)
+ SendMessage(window->hwnd, WM_USER_SET_MINSIZE, 0, 0);
+ if (window->ismaximized)
+ SendMessage(window->hwnd, WM_USER_SET_MAXSIZE, 0, 0);
+ }
+ }
+
+ // if we're visible and running and not in the middle of a resize, draw
+ if (window->hwnd != NULL && window->target != NULL)
+ {
+ int got_lock = TRUE;
+
+ mtlog_add("winwindow_video_window_update: try lock");
+
+ // only block if we're throttled
+ if (video_get_throttle() || timeGetTime() - last_update_time > 250)
+ osd_lock_acquire(window->render_lock);
+ else
+ got_lock = osd_lock_try(window->render_lock);
+
+ // only render if we were able to get the lock
+ if (got_lock)
+ {
+ const render_primitive_list *primlist;
+
+ mtlog_add("winwindow_video_window_update: got lock");
+
+ // don't hold the lock; we just used it to see if rendering was still happening
+ osd_lock_release(window->render_lock);
+
+ // ensure the target bounds are up-to-date, and then get the primitives
+ primlist = (*draw.window_get_primitives)(window);
+
+ // post a redraw request with the primitive list as a parameter
+ last_update_time = timeGetTime();
+ mtlog_add("winwindow_video_window_update: PostMessage start");
+ if (multithreading_enabled)
+ PostMessage(window->hwnd, WM_USER_REDRAW, 0, (LPARAM)primlist);
+ else
+ SendMessage(window->hwnd, WM_USER_REDRAW, 0, (LPARAM)primlist);
+ mtlog_add("winwindow_video_window_update: PostMessage end");
+ }
+ }
+
+ mtlog_add("winwindow_video_window_update: end");
+}
+
+
+
+//============================================================
+// winwindow_video_window_monitor
+// (window thread)
+//============================================================
+
+win_monitor_info *winwindow_video_window_monitor(win_window_info *window, const RECT *proposed)
+{
+ win_monitor_info *monitor;
+
+ // in window mode, find the nearest
+ if (!window->fullscreen)
+ {
+ if (proposed != NULL)
+ monitor = winvideo_monitor_from_handle(MonitorFromRect(proposed, MONITOR_DEFAULTTONEAREST));
+ else
+ monitor = winvideo_monitor_from_handle(MonitorFromWindow(window->hwnd, MONITOR_DEFAULTTONEAREST));
+ }
+
+ // in full screen, just use the configured monitor
+ else
+ monitor = window->monitor;
+
+ // make sure we're up-to-date
+ winvideo_monitor_refresh(monitor);
+ return monitor;
+}
+
+
+
+//============================================================
+// create_window_class
+// (main thread)
+//============================================================
+
+static void create_window_class(void)
+{
+ static int classes_created = FALSE;
+
+ assert(GetCurrentThreadId() == main_threadid);
+
+ if (!classes_created)
+ {
+ WNDCLASS wc = { 0 };
+
+ // initialize the description of the window class
+ wc.lpszClassName = TEXT("MAME");
+ wc.hInstance = GetModuleHandle(NULL);
+#ifdef MESS
+ wc.lpfnWndProc = win_mess_window_proc;
+#else
+ wc.lpfnWndProc = winwindow_video_window_proc;
+#endif
+ wc.hCursor = LoadCursor(NULL, IDC_ARROW);
+ wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
+
+ // register the class; fail if we can't
+ if (!RegisterClass(&wc))
+ fatalerror("Failed to create window class");
+ classes_created = TRUE;
+ }
+}
+
+
+
+//============================================================
+// set_starting_view
+// (main thread)
+//============================================================
+
+static void set_starting_view(int index, win_window_info *window, const char *view)
+{
+ const char *defview = options_get_string(mame_options(), WINOPTION_VIEW);
+ int viewindex = -1;
+
+ assert(GetCurrentThreadId() == main_threadid);
+
+ // choose non-auto over auto
+ if (strcmp(view, "auto") == 0 && strcmp(defview, "auto") != 0)
+ view = defview;
+
+ // auto view just selects the nth view
+ if (strcmp(view, "auto") != 0)
+ {
+ // scan for a matching view name
+ for (viewindex = 0; ; viewindex++)
+ {
+ const char *name = render_target_get_view_name(window->target, viewindex);
+
+ // stop scanning if we hit NULL
+ if (name == NULL)
+ {
+ viewindex = -1;
+ break;
+ }
+ if (mame_strnicmp(name, view, strlen(view)) == 0)
+ break;
+ }
+ }
+
+ // if we don't have a match, default to the nth view
+ if (viewindex == -1)
+ {
+ int scrcount;
+
+ // count the number of screens
+ for (scrcount = 0; Machine->drv->screen[scrcount].tag != NULL; scrcount++) ;
+
+ // if we have enough screens to be one per monitor, assign in order
+ if (video_config.numscreens >= scrcount)
+ {
+ // find the first view with this screen and this screen only
+ for (viewindex = 0; ; viewindex++)
+ {
+ UINT32 viewscreens = render_target_get_view_screens(window->target, viewindex);
+ if (viewscreens == (1 << index))
+ break;
+ if (viewscreens == 0)
+ {
+ viewindex = -1;
+ break;
+ }
+ }
+ }
+
+ // otherwise, find the first view that has all the screens
+ if (viewindex == -1)
+ {
+ for (viewindex = 0; ; viewindex++)
+ {
+ UINT32 viewscreens = render_target_get_view_screens(window->target, viewindex);
+ if (viewscreens == (1 << scrcount) - 1)
+ break;
+ if (viewscreens == 0)
+ break;
+ }
+ }
+ }
+
+ // make sure it's a valid view
+ if (render_target_get_view_name(window->target, viewindex) == NULL)
+ viewindex = 0;
+
+ // set the view
+ render_target_set_view(window->target, viewindex);
+}
+
+
+
+//============================================================
+// winwindow_ui_pause_from_main_thread
+// (main thread)
+//============================================================
+
+void winwindow_ui_pause_from_main_thread(int pause)
+{
+ int old_temp_pause = ui_temp_pause;
+
+ assert(GetCurrentThreadId() == main_threadid);
+
+ // if we're pausing, increment the pause counter
+ if (pause)
+ {
+ // if we're the first to pause, we have to actually initiate it
+ if (ui_temp_pause++ == 0)
+ {
+ // only call mame_pause if we weren't already paused due to some external reason
+ ui_temp_was_paused = mame_is_paused(Machine);
+ if (!ui_temp_was_paused)
+ mame_pause(Machine, TRUE);
+
+ SetEvent(ui_pause_event);
+ }
+ }
+
+ // if we're resuming, decrement the pause counter
+ else
+ {
+ // if we're the last to resume, unpause MAME
+ if (--ui_temp_pause == 0)
+ {
+ // but only do it if we were the ones who initiated it
+ if (!ui_temp_was_paused)
+ mame_pause(Machine, FALSE);
+
+ ResetEvent(ui_pause_event);
+ }
+ }
+
+ if (LOG_TEMP_PAUSE)
+ logerror("winwindow_ui_pause_from_main_thread(): %d --> %d\n", old_temp_pause, ui_temp_pause);
+}
+
+
+
+//============================================================
+// winwindow_ui_pause_from_window_thread
+// (window thread)
+//============================================================
+
+void winwindow_ui_pause_from_window_thread(int pause)
+{
+ assert(GetCurrentThreadId() == window_threadid);
+
+ // if we're multithreaded, we have to request a pause on the main thread
+ if (multithreading_enabled)
+ {
+ // request a pause from the main thread
+ PostThreadMessage(main_threadid, WM_USER_UI_TEMP_PAUSE, pause, 0);
+
+ // if we're pausing, block until it happens
+ if (pause)
+ WaitForSingleObject(ui_pause_event, INFINITE);
+ }
+
+ // otherwise, we just do it directly
+ else
+ winwindow_ui_pause_from_main_thread(pause);
+}
+
+
+
+//============================================================
+// winwindow_ui_exec_on_main_thread
+// (window thread)
+//============================================================
+
+void winwindow_ui_exec_on_main_thread(void (*func)(void *), void *param)
+{
+ assert(GetCurrentThreadId() == window_threadid);
+
+ // if we're multithreaded, we have to request a pause on the main thread
+ if (multithreading_enabled)
+ {
+ // request a pause from the main thread
+ PostThreadMessage(main_threadid, WM_USER_EXEC_FUNC, (WPARAM) func, (LPARAM) param);
+ }
+
+ // otherwise, we just do it directly
+ else
+ (*func)(param);
+}
+
+
+
+//============================================================
+// winwindow_ui_is_paused
+//============================================================
+
+int winwindow_ui_is_paused(void)
+{
+ return mame_is_paused(Machine) && ui_temp_was_paused;
+}
+
+
+
+//============================================================
+// wnd_extra_width
+// (window thread)
+//============================================================
+
+INLINE int wnd_extra_width(win_window_info *window)
+{
+ RECT temprect = { 100, 100, 200, 200 };
+ if (window->fullscreen)
+ return 0;
+ AdjustWindowRectEx(&temprect, WINDOW_STYLE, win_has_menu(window), WINDOW_STYLE_EX);
+ return rect_width(&temprect) - 100;
+}
+
+
+
+//============================================================
+// wnd_extra_height
+// (window thread)
+//============================================================
+
+INLINE int wnd_extra_height(win_window_info *window)
+{
+ RECT temprect = { 100, 100, 200, 200 };
+ if (window->fullscreen)
+ return 0;
+ AdjustWindowRectEx(&temprect, WINDOW_STYLE, win_has_menu(window), WINDOW_STYLE_EX);
+ return rect_height(&temprect) - 100;
+}
+
+
+
+//============================================================
+// thread_entry
+// (window thread)
+//============================================================
+
+static unsigned __stdcall thread_entry(void *param)
+{
+ MSG message;
+
+ // make a bogus user call to make us a message thread
+ PeekMessage(&message, NULL, 0, 0, PM_NOREMOVE);
+
+ // attach our input to the main thread
+ AttachThreadInput(main_threadid, window_threadid, TRUE);
+
+ // signal to the main thread that we are ready to receive events
+ SetEvent(window_thread_ready_event);
+
+ // run the message pump
+ while (GetMessage(&message, NULL, 0, 0))
+ {
+ int dispatch = TRUE;
+
+ if ((message.hwnd == NULL) || is_mame_window(message.hwnd))
+ {
+ switch (message.message)
+ {
+ // ignore input messages here
+ case WM_SYSKEYUP:
+ case WM_SYSKEYDOWN:
+#ifndef MESS
+ case WM_KEYUP:
+ case WM_KEYDOWN:
+ case WM_CHAR:
+#endif // MESS
+ dispatch = FALSE;
+ break;
+
+ // forward mouse button downs to the input system
+ case WM_LBUTTONDOWN:
+ wininput_handle_mouse_button(0, TRUE, GET_X_LPARAM(message.lParam), GET_Y_LPARAM(message.lParam));
+ dispatch = FALSE;
+ break;
+
+ case WM_RBUTTONDOWN:
+ wininput_handle_mouse_button(1, TRUE, GET_X_LPARAM(message.lParam), GET_Y_LPARAM(message.lParam));
+ dispatch = FALSE;
+ break;
+
+ case WM_MBUTTONDOWN:
+ wininput_handle_mouse_button(2, TRUE, GET_X_LPARAM(message.lParam), GET_Y_LPARAM(message.lParam));
+ dispatch = FALSE;
+ break;
+
+ case WM_XBUTTONDOWN:
+ wininput_handle_mouse_button(3, TRUE, GET_X_LPARAM(message.lParam), GET_Y_LPARAM(message.lParam));
+ dispatch = FALSE;
+ break;
+
+ // forward mouse button ups to the input system
+ case WM_LBUTTONUP:
+ wininput_handle_mouse_button(0, FALSE, GET_X_LPARAM(message.lParam), GET_Y_LPARAM(message.lParam));
+ dispatch = FALSE;
+ break;
+
+ case WM_RBUTTONUP:
+ wininput_handle_mouse_button(1, FALSE, GET_X_LPARAM(message.lParam), GET_Y_LPARAM(message.lParam));
+ dispatch = FALSE;
+ break;
+
+ case WM_MBUTTONUP:
+ wininput_handle_mouse_button(2, FALSE, GET_X_LPARAM(message.lParam), GET_Y_LPARAM(message.lParam));
+ dispatch = FALSE;
+ break;
+
+ case WM_XBUTTONUP:
+ wininput_handle_mouse_button(3, FALSE, GET_X_LPARAM(message.lParam), GET_Y_LPARAM(message.lParam));
+ dispatch = FALSE;
+ break;
+
+ // a terminate message to the thread posts a quit
+ case WM_USER_SELF_TERMINATE:
+ PostQuitMessage(0);
+ dispatch = FALSE;
+ break;
+
+ // handle the "complete create" message
+ case WM_USER_FINISH_CREATE_WINDOW:
+ {
+ win_window_info *window = (win_window_info *)message.lParam;
+ window->init_state = complete_create(window) ? -1 : 1;
+ dispatch = FALSE;
+ break;
+ }
+ }
+ }
+
+ // dispatch if necessary
+ if (dispatch)
+ {
+ TranslateMessage(&message);
+ DispatchMessage(&message);
+ }
+ }
+ return 0;
+}
+
+
+
+//============================================================
+// complete_create
+// (window thread)
+//============================================================
+
+static int complete_create(win_window_info *window)
+{
+ RECT monitorbounds, client;
+ int tempwidth, tempheight;
+ HMENU menu = NULL;
+ HDC dc;
+
+ assert(GetCurrentThreadId() == window_threadid);
+
+ // get the monitor bounds
+ monitorbounds = window->monitor->info.rcMonitor;
+
+ // create the window menu if needed
+#if HAS_WINDOW_MENU
+ if (win_create_menu(&menu))
+ return 1;
+#endif
+
+ // create the window, but don't show it yet
+ window->hwnd = win_create_window_ex_utf8(
+ window->fullscreen ? FULLSCREEN_STYLE_EX : WINDOW_STYLE_EX,
+ "MAME",
+ window->title,
+ window->fullscreen ? FULLSCREEN_STYLE : WINDOW_STYLE,
+ monitorbounds.left + 20, monitorbounds.top + 20,
+ monitorbounds.left + 100, monitorbounds.top + 100,
+ NULL,//(win_window_list != NULL) ? win_window_list->hwnd : NULL,
+ menu,
+ GetModuleHandle(NULL),
+ NULL);
+ if (window->hwnd == NULL)
+ return 1;
+
+ // set a pointer back to us
+ SetWindowLongPtr(window->hwnd, GWLP_USERDATA, (LONG_PTR)window);
+
+ // skip the positioning stuff for -video none */
+ if (video_config.mode == VIDEO_MODE_NONE)
+ return 0;
+
+ // adjust the window position to the initial width/height
+ tempwidth = (window->maxwidth != 0) ? window->maxwidth : 640;
+ tempheight = (window->maxheight != 0) ? window->maxheight : 480;
+ SetWindowPos(window->hwnd, NULL, monitorbounds.left + 20, monitorbounds.top + 20,
+ monitorbounds.left + tempwidth + wnd_extra_width(window),
+ monitorbounds.top + tempheight + wnd_extra_height(window),
+ SWP_NOZORDER);
+
+ // maximum or minimize as appropriate
+ if (window->startmaximized)
+ maximize_window(window);
+ else
+ minimize_window(window);
+ adjust_window_position_after_major_change(window);
+
+ // show the window
+ if (!window->fullscreen || window->fullscreen_safe)
+ {
+ // finish off by trying to initialize DirectX; if we fail, ignore it
+ if ((*draw.window_init)(window))
+ return 1;
+ ShowWindow(window->hwnd, SW_SHOW);
+ }
+
+ // clear the window
+ dc = GetDC(window->hwnd);
+ GetClientRect(window->hwnd, &client);
+ FillRect(dc, &client, (HBRUSH)GetStockObject(BLACK_BRUSH));
+ ReleaseDC(window->hwnd, dc);
+ return 0;
+}
+
+
+
+//============================================================
+// winwindow_video_window_proc
+// (window thread)
+//============================================================
+
+LRESULT CALLBACK winwindow_video_window_proc(HWND wnd, UINT message, WPARAM wparam, LPARAM lparam)
+{
+ LONG_PTR ptr = GetWindowLongPtr(wnd, GWLP_USERDATA);
+ win_window_info *window = (win_window_info *)ptr;
+
+ // we may get called before SetWindowLongPtr is called
+ if (window != NULL)
+ {
+ assert(GetCurrentThreadId() == window_threadid);
+ update_minmax_state(window);
+ }
+
+ // handle a few messages
+ switch (message)
+ {
+ // paint: redraw the last bitmap
+ case WM_PAINT:
+ {
+ PAINTSTRUCT pstruct;
+ HDC hdc = BeginPaint(wnd, &pstruct);
+ draw_video_contents(window, hdc, TRUE);
+ if (win_has_menu(window))
+ DrawMenuBar(window->hwnd);
+ EndPaint(wnd, &pstruct);
+ break;
+ }
+
+ // non-client paint: punt if full screen
+ case WM_NCPAINT:
+ if (!window->fullscreen || HAS_WINDOW_MENU)
+ return DefWindowProc(wnd, message, wparam, lparam);
+ break;
+
+ // input: handle the raw input
+ case WM_INPUT:
+ wininput_handle_raw((HRAWINPUT)lparam);
+ break;
+
+ // syskeys - ignore
+ case WM_SYSKEYUP:
+ case WM_SYSKEYDOWN:
+ break;
+
+ // pause the system when we start a menu or resize
+ case WM_ENTERSIZEMOVE:
+ window->resize_state = RESIZE_STATE_RESIZING;
+ case WM_ENTERMENULOOP:
+ winwindow_ui_pause_from_window_thread(TRUE);
+ break;
+
+ // unpause the system when we stop a menu or resize and force a redraw
+ case WM_EXITSIZEMOVE:
+ window->resize_state = RESIZE_STATE_PENDING;
+ case WM_EXITMENULOOP:
+ winwindow_ui_pause_from_window_thread(FALSE);
+ InvalidateRect(wnd, NULL, FALSE);
+ break;
+
+ // get min/max info: set the minimum window size
+ case WM_GETMINMAXINFO:
+ {
+ MINMAXINFO *minmax = (MINMAXINFO *)lparam;
+ minmax->ptMinTrackSize.x = MIN_WINDOW_DIM;
+ minmax->ptMinTrackSize.y = MIN_WINDOW_DIM;
+ break;
+ }
+
+ // sizing: constrain to the aspect ratio unless control key is held down
+ case WM_SIZING:
+ {
+ RECT *rect = (RECT *)lparam;
+ if (video_config.keepaspect && !(GetAsyncKeyState(VK_CONTROL) & 0x8000))
+ constrain_to_aspect_ratio(window, rect, wparam);
+ InvalidateRect(wnd, NULL, FALSE);
+ break;
+ }
+
+ // syscommands: catch win_start_maximized
+ case WM_SYSCOMMAND:
+ {
+ // prevent screensaver or monitor power events
+ if (wparam == SC_MONITORPOWER || wparam == SC_SCREENSAVE)
+ return 1;
+
+ // most SYSCOMMANDs require us to invalidate the window
+ InvalidateRect(wnd, NULL, FALSE);
+
+ // handle maximize
+ if ((wparam & 0xfff0) == SC_MAXIMIZE)
+ {
+ update_minmax_state(window);
+ if (window->ismaximized)
+ minimize_window(window);
+ else
+ maximize_window(window);
+ break;
+ }
+ return DefWindowProc(wnd, message, wparam, lparam);
+ }
+
+ // track whether we are in the foreground
+ case WM_ACTIVATEAPP:
+ in_background = !wparam;
+ break;
+
+ // close: cause MAME to exit
+ case WM_CLOSE:
+ if (multithreading_enabled)
+ PostThreadMessage(main_threadid, WM_QUIT, 0, 0);
+ else
+ mame_schedule_exit(Machine);
+ break;
+
+ // destroy: clean up all attached rendering bits and NULL out our hwnd
+ case WM_DESTROY:
+ (*draw.window_destroy)(window);
+ window->hwnd = NULL;
+ return DefWindowProc(wnd, message, wparam, lparam);
+
+ // self redraw: draw ourself in a non-painty way
+ case WM_USER_REDRAW:
+ {
+ HDC hdc = GetDC(wnd);
+
+ mtlog_add("winwindow_video_window_proc: WM_USER_REDRAW begin");
+ window->primlist = (const render_primitive_list *)lparam;
+ draw_video_contents(window, hdc, FALSE);
+ mtlog_add("winwindow_video_window_proc: WM_USER_REDRAW end");
+
+ ReleaseDC(wnd, hdc);
+ break;
+ }
+
+ // self destruct
+ case WM_USER_SELF_TERMINATE:
+ DestroyWindow(window->hwnd);
+ break;
+
+ // fullscreen set
+ case WM_USER_SET_FULLSCREEN:
+ set_fullscreen(window, wparam);
+ break;
+
+ // minimum size set
+ case WM_USER_SET_MINSIZE:
+ minimize_window(window);
+ break;
+
+ // maximum size set
+ case WM_USER_SET_MAXSIZE:
+ maximize_window(window);
+ break;
+
+ // set focus: if we're not the primary window, switch back
+ // commented out ATM because this prevents us from resizing secondary windows
+// case WM_SETFOCUS:
+// if (window != win_window_list && win_window_list != NULL)
+// SetFocus(win_window_list->hwnd);
+// break;
+
+ // everything else: defaults
+ default:
+ return DefWindowProc(wnd, message, wparam, lparam);
+ }
+
+ return 0;
+}
+
+
+
+//============================================================
+// draw_video_contents
+// (window thread)
+//============================================================
+
+static void draw_video_contents(win_window_info *window, HDC dc, int update)
+{
+ assert(GetCurrentThreadId() == window_threadid);
+
+ mtlog_add("draw_video_contents: begin");
+
+ mtlog_add("draw_video_contents: render lock acquire");
+ osd_lock_acquire(window->render_lock);
+ mtlog_add("draw_video_contents: render lock acquired");
+
+ // if we're iconic, don't bother
+ if (window->hwnd != NULL && !IsIconic(window->hwnd))
+ {
+ // if no bitmap, just fill
+ if (window->primlist == NULL)
+ {
+ RECT fill;
+ GetClientRect(window->hwnd, &fill);
+ FillRect(dc, &fill, (HBRUSH)GetStockObject(BLACK_BRUSH));
+ }
+
+ // otherwise, render with our drawing system
+ else
+ {
+ (*draw.window_draw)(window, dc, update);
+ mtlog_add("draw_video_contents: drawing finished");
+ }
+ }
+
+ osd_lock_release(window->render_lock);
+ mtlog_add("draw_video_contents: render lock released");
+
+ mtlog_add("draw_video_contents: end");
+}
+
+
+
+//============================================================
+// constrain_to_aspect_ratio
+// (window thread)
+//============================================================
+
+static void constrain_to_aspect_ratio(win_window_info *window, RECT *rect, int adjustment)
+{
+ win_monitor_info *monitor = winwindow_video_window_monitor(window, rect);
+ INT32 extrawidth = wnd_extra_width(window);
+ INT32 extraheight = wnd_extra_height(window);
+ INT32 propwidth, propheight;
+ INT32 minwidth, minheight;
+ INT32 maxwidth, maxheight;
+ INT32 viswidth, visheight;
+ INT32 adjwidth, adjheight;
+ float pixel_aspect;
+
+ assert(GetCurrentThreadId() == window_threadid);
+
+ // get the pixel aspect ratio for the target monitor
+ pixel_aspect = winvideo_monitor_get_aspect(monitor);
+
+ // determine the proposed width/height
+ propwidth = rect_width(rect) - extrawidth;
+ propheight = rect_height(rect) - extraheight;
+
+ // based on which edge we are adjusting, take either the width, height, or both as gospel
+ // and scale to fit using that as our parameter
+ switch (adjustment)
+ {
+ case WMSZ_BOTTOM:
+ case WMSZ_TOP:
+ render_target_compute_visible_area(window->target, 10000, propheight, pixel_aspect, render_target_get_orientation(window->target), &propwidth, &propheight);
+ break;
+
+ case WMSZ_LEFT:
+ case WMSZ_RIGHT:
+ render_target_compute_visible_area(window->target, propwidth, 10000, pixel_aspect, render_target_get_orientation(window->target), &propwidth, &propheight);
+ break;
+
+ default:
+ render_target_compute_visible_area(window->target, propwidth, propheight, pixel_aspect, render_target_get_orientation(window->target), &propwidth, &propheight);
+ break;
+ }
+
+ // get the minimum width/height for the current layout
+ render_target_get_minimum_size(window->target, &minwidth, &minheight);
+
+ // clamp against the absolute minimum
+ propwidth = MAX(propwidth, MIN_WINDOW_DIM);
+ propheight = MAX(propheight, MIN_WINDOW_DIM);
+
+ // clamp against the minimum width and height
+ propwidth = MAX(propwidth, minwidth);
+ propheight = MAX(propheight, minheight);
+
+ // clamp against the maximum (fit on one screen for full screen mode)
+ if (window->fullscreen)
+ {
+ maxwidth = rect_width(&monitor->info.rcMonitor) - extrawidth;
+ maxheight = rect_height(&monitor->info.rcMonitor) - extraheight;
+ }
+ else
+ {
+ maxwidth = rect_width(&monitor->info.rcWork) - extrawidth;
+ maxheight = rect_height(&monitor->info.rcWork) - extraheight;
+
+ // further clamp to the maximum width/height in the window
+ if (window->maxwidth != 0)
+ maxwidth = MIN(maxwidth, window->maxwidth + extrawidth);
+ if (window->maxheight != 0)
+ maxheight = MIN(maxheight, window->maxheight + extraheight);
+ }
+
+ // clamp to the maximum
+ propwidth = MIN(propwidth, maxwidth);
+ propheight = MIN(propheight, maxheight);
+
+ // compute the visible area based on the proposed rectangle
+ render_target_compute_visible_area(window->target, propwidth, propheight, pixel_aspect, render_target_get_orientation(window->target), &viswidth, &visheight);
+
+ // compute the adjustments we need to make
+ adjwidth = (viswidth + extrawidth) - rect_width(rect);
+ adjheight = (visheight + extraheight) - rect_height(rect);
+
+ // based on which corner we're adjusting, constrain in different ways
+ switch (adjustment)
+ {
+ case WMSZ_BOTTOM:
+ case WMSZ_BOTTOMRIGHT:
+ case WMSZ_RIGHT:
+ rect->right += adjwidth;
+ rect->bottom += adjheight;
+ break;
+
+ case WMSZ_BOTTOMLEFT:
+ rect->left -= adjwidth;
+ rect->bottom += adjheight;
+ break;
+
+ case WMSZ_LEFT:
+ case WMSZ_TOPLEFT:
+ case WMSZ_TOP:
+ rect->left -= adjwidth;
+ rect->top -= adjheight;
+ break;
+
+ case WMSZ_TOPRIGHT:
+ rect->right += adjwidth;
+ rect->top -= adjheight;
+ break;
+ }
+}
+
+
+
+//============================================================
+// get_min_bounds
+// (window thread)
+//============================================================
+
+static void get_min_bounds(win_window_info *window, RECT *bounds, int constrain)
+{
+ INT32 minwidth, minheight;
+
+ assert(GetCurrentThreadId() == window_threadid);
+
+ // get the minimum target size
+ render_target_get_minimum_size(window->target, &minwidth, &minheight);
+
+ // expand to our minimum dimensions
+ if (minwidth < MIN_WINDOW_DIM)
+ minwidth = MIN_WINDOW_DIM;
+ if (minheight < MIN_WINDOW_DIM)
+ minheight = MIN_WINDOW_DIM;
+
+ // account for extra window stuff
+ minwidth += wnd_extra_width(window);
+ minheight += wnd_extra_height(window);
+
+ // if we want it constrained, figure out which one is larger
+ if (constrain)
+ {
+ RECT test1, test2;
+
+ // first constrain with no height limit
+ test1.top = test1.left = 0;
+ test1.right = minwidth;
+ test1.bottom = 10000;
+ constrain_to_aspect_ratio(window, &test1, WMSZ_BOTTOMRIGHT);
+
+ // then constrain with no width limit
+ test2.top = test2.left = 0;
+ test2.right = 10000;
+ test2.bottom = minheight;
+ constrain_to_aspect_ratio(window, &test2, WMSZ_BOTTOMRIGHT);
+
+ // pick the larger
+ if (rect_width(&test1) > rect_width(&test2))
+ {
+ minwidth = rect_width(&test1);
+ minheight = rect_height(&test1);
+ }
+ else
+ {
+ minwidth = rect_width(&test2);
+ minheight = rect_height(&test2);
+ }
+ }
+
+ // get the window rect
+ GetWindowRect(window->hwnd, bounds);
+
+ // now adjust
+ bounds->right = bounds->left + minwidth;
+ bounds->bottom = bounds->top + minheight;
+}
+
+
+
+//============================================================
+// get_max_bounds
+// (window thread)
+//============================================================
+
+static void get_max_bounds(win_window_info *window, RECT *bounds, int constrain)
+{
+ RECT maximum;
+
+ assert(GetCurrentThreadId() == window_threadid);
+
+ // compute the maximum client area
+ winvideo_monitor_refresh(window->monitor);
+ maximum = window->monitor->info.rcWork;
+
+ // clamp to the window's max
+ if (window->maxwidth != 0)
+ {
+ int temp = window->maxwidth + wnd_extra_width(window);
+ if (temp < rect_width(&maximum))
+ maximum.right = maximum.left + temp;
+ }
+ if (window->maxheight != 0)
+ {
+ int temp = window->maxheight + wnd_extra_height(window);
+ if (temp < rect_height(&maximum))
+ maximum.bottom = maximum.top + temp;
+ }
+
+ // constrain to fit
+ if (constrain)
+ constrain_to_aspect_ratio(window, &maximum, WMSZ_BOTTOMRIGHT);
+ else
+ {
+ maximum.right -= wnd_extra_width(window);
+ maximum.bottom -= wnd_extra_height(window);
+ }
+
+ // center within the work area
+ bounds->left = window->monitor->info.rcWork.left + (rect_width(&window->monitor->info.rcWork) - rect_width(&maximum)) / 2;
+ bounds->top = window->monitor->info.rcWork.top + (rect_height(&window->monitor->info.rcWork) - rect_height(&maximum)) / 2;
+ bounds->right = bounds->left + rect_width(&maximum);
+ bounds->bottom = bounds->top + rect_height(&maximum);
+}
+
+
+
+//============================================================
+// update_minmax_state
+// (window thread)
+//============================================================
+
+static void update_minmax_state(win_window_info *window)
+{
+ assert(GetCurrentThreadId() == window_threadid);
+
+ if (!window->fullscreen)
+ {
+ RECT bounds, minbounds, maxbounds;
+
+ // compare the maximum bounds versus the current bounds
+ get_min_bounds(window, &minbounds, video_config.keepaspect);
+ get_max_bounds(window, &maxbounds, video_config.keepaspect);
+ GetWindowRect(window->hwnd, &bounds);
+
+ // if either the width or height matches, we were maximized
+ window->isminimized = (rect_width(&bounds) == rect_width(&minbounds) ||
+ rect_height(&bounds) == rect_height(&minbounds));
+ window->ismaximized = (rect_width(&bounds) == rect_width(&maxbounds) ||
+ rect_height(&bounds) == rect_height(&maxbounds));
+ }
+ else
+ {
+ window->isminimized = FALSE;
+ window->ismaximized = TRUE;
+ }
+}
+
+
+
+//============================================================
+// minimize_window
+// (window thread)
+//============================================================
+
+static void minimize_window(win_window_info *window)
+{
+ RECT newsize;
+
+ assert(GetCurrentThreadId() == window_threadid);
+
+ get_min_bounds(window, &newsize, video_config.keepaspect);
+ SetWindowPos(window->hwnd, NULL, newsize.left, newsize.top, rect_width(&newsize), rect_height(&newsize), SWP_NOZORDER);
+}
+
+
+
+//============================================================
+// maximize_window
+// (window thread)
+//============================================================
+
+static void maximize_window(win_window_info *window)
+{
+ RECT newsize;
+
+ assert(GetCurrentThreadId() == window_threadid);
+
+ get_max_bounds(window, &newsize, video_config.keepaspect);
+ SetWindowPos(window->hwnd, NULL, newsize.left, newsize.top, rect_width(&newsize), rect_height(&newsize), SWP_NOZORDER);
+}
+
+
+
+//============================================================
+// adjust_window_position_after_major_change
+// (window thread)
+//============================================================
+
+static void adjust_window_position_after_major_change(win_window_info *window)
+{
+ RECT oldrect, newrect;
+
+ assert(GetCurrentThreadId() == window_threadid);
+
+ // get the current size
+ GetWindowRect(window->hwnd, &oldrect);
+
+ // adjust the window size so the client area is what we want
+ if (!window->fullscreen)
+ {
+ // constrain the existing size to the aspect ratio
+ newrect = oldrect;
+ if (video_config.keepaspect)
+ constrain_to_aspect_ratio(window, &newrect, WMSZ_BOTTOMRIGHT);
+ }
+
+ // in full screen, make sure it covers the primary display
+ else
+ {
+ win_monitor_info *monitor = winwindow_video_window_monitor(window, NULL);
+ newrect = monitor->info.rcMonitor;
+ }
+
+ // adjust the position if different
+ if (oldrect.left != newrect.left || oldrect.top != newrect.top ||
+ oldrect.right != newrect.right || oldrect.bottom != newrect.bottom)
+ SetWindowPos(window->hwnd, window->fullscreen ? HWND_TOPMOST : HWND_TOP,
+ newrect.left, newrect.top,
+ rect_width(&newrect), rect_height(&newrect), 0);
+
+ // take note of physical window size (used for lightgun coordinate calculation)
+ if (window == win_window_list)
+ {
+ win_physical_width = rect_width(&newrect);
+ win_physical_height = rect_height(&newrect);
+ logerror("Physical width %d, height %d\n",win_physical_width,win_physical_height);
+ }
+}
+
+
+
+//============================================================
+// set_fullscreen
+// (window thread)
+//============================================================
+
+static void set_fullscreen(win_window_info *window, int fullscreen)
+{
+ assert(GetCurrentThreadId() == window_threadid);
+
+ // if we're in the right state, punt
+ if (window->fullscreen == fullscreen)
+ return;
+ window->fullscreen = fullscreen;
+
+ // kill off the drawers
+ (*draw.window_destroy)(window);
+
+ // hide ourself
+ ShowWindow(window->hwnd, SW_HIDE);
+
+ // configure the window if non-fullscreen
+ if (!fullscreen)
+ {
+ // adjust the style
+ SetWindowLong(window->hwnd, GWL_STYLE, WINDOW_STYLE);
+ SetWindowLong(window->hwnd, GWL_EXSTYLE, WINDOW_STYLE_EX);
+ SetWindowPos(window->hwnd, 0, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED);
+
+ // force to the bottom, then back on top
+ SetWindowPos(window->hwnd, HWND_BOTTOM, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);
+ SetWindowPos(window->hwnd, HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);
+
+ // if we have previous non-fullscreen bounds, use those
+ if (window->non_fullscreen_bounds.right != window->non_fullscreen_bounds.left)
+ {
+ SetWindowPos(window->hwnd, HWND_TOP, window->non_fullscreen_bounds.left, window->non_fullscreen_bounds.top,
+ rect_width(&window->non_fullscreen_bounds), rect_height(&window->non_fullscreen_bounds),
+ SWP_NOZORDER);
+ }
+
+ // otherwise, set a small size and maximize from there
+ else
+ {
+ SetWindowPos(window->hwnd, HWND_TOP, 0, 0, MIN_WINDOW_DIM, MIN_WINDOW_DIM, SWP_NOZORDER);
+ maximize_window(window);
+ }
+ }
+
+ // configure the window if fullscreen
+ else
+ {
+ // save the bounds
+ GetWindowRect(window->hwnd, &window->non_fullscreen_bounds);
+
+ // adjust the style
+ SetWindowLong(window->hwnd, GWL_STYLE, FULLSCREEN_STYLE);
+ SetWindowLong(window->hwnd, GWL_EXSTYLE, FULLSCREEN_STYLE_EX);
+ SetWindowPos(window->hwnd, 0, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED);
+
+ // set topmost
+ SetWindowPos(window->hwnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);
+ }
+
+ // adjust the window to compensate for the change
+ adjust_window_position_after_major_change(window);
+
+ // show ourself
+ if (!window->fullscreen || window->fullscreen_safe)
+ {
+ if (video_config.mode != VIDEO_MODE_NONE)
+ ShowWindow(window->hwnd, SW_SHOW);
+ if ((*draw.window_init)(window))
+ exit(1);
+ }
+
+ // ensure we're still adjusted correctly
+ adjust_window_position_after_major_change(window);
+}
diff --git a/src/osd/windows/window.h b/src/osd/windows/window.h
new file mode 100644
index 00000000000..31684704e0c
--- /dev/null
+++ b/src/osd/windows/window.h
@@ -0,0 +1,160 @@
+//============================================================
+//
+// window.h - Win32 window handling
+//
+// Copyright (c) 1996-2007, Nicola Salmoria and the MAME Team.
+// Visit http://mamedev.org for licensing and usage restrictions.
+//
+//============================================================
+
+#ifndef __WIN_WINDOW__
+#define __WIN_WINDOW__
+
+#include "video.h"
+#include "render.h"
+
+
+//============================================================
+// PARAMETERS
+//============================================================
+
+#ifndef MESS
+#define HAS_WINDOW_MENU FALSE
+#else
+#define HAS_WINDOW_MENU TRUE
+#endif
+
+
+
+//============================================================
+// CONSTANTS
+//============================================================
+
+#define RESIZE_STATE_NORMAL 0
+#define RESIZE_STATE_RESIZING 1
+#define RESIZE_STATE_PENDING 2
+
+
+
+//============================================================
+// TYPE DEFINITIONS
+//============================================================
+
+typedef struct _win_window_info win_window_info;
+struct _win_window_info
+{
+ win_window_info * next;
+ volatile int init_state;
+
+ // window handle and info
+ HWND hwnd;
+ char title[256];
+ RECT non_fullscreen_bounds;
+ int startmaximized;
+ int isminimized;
+ int ismaximized;
+ int resize_state;
+
+ // monitor info
+ win_monitor_info * monitor;
+ int fullscreen;
+ int fullscreen_safe;
+ int maxwidth, maxheight;
+ int refresh;
+ float aspect;
+
+ // rendering info
+ osd_lock * render_lock;
+ render_target * target;
+ int targetview;
+ int targetorient;
+ int targetlayerconfig;
+ const render_primitive_list *primlist;
+
+ // drawing data
+ void * drawdata;
+};
+
+
+typedef struct _win_draw_callbacks win_draw_callbacks;
+struct _win_draw_callbacks
+{
+ void (*exit)(void);
+
+ int (*window_init)(win_window_info *window);
+ const render_primitive_list *(*window_get_primitives)(win_window_info *window);
+ int (*window_draw)(win_window_info *window, HDC dc, int update);
+ void (*window_destroy)(win_window_info *window);
+};
+
+
+
+//============================================================
+// GLOBAL VARIABLES
+//============================================================
+
+// windows
+extern win_window_info *win_window_list;
+
+
+
+//============================================================
+// PROTOTYPES
+//============================================================
+
+// core initialization
+void winwindow_init(running_machine *machine);
+
+// creation/deletion of windows
+void winwindow_video_window_create(int index, win_monitor_info *monitor, const win_window_config *config);
+
+BOOL winwindow_has_focus(void);
+void winwindow_update_cursor_state(void);
+void winwindow_video_window_update(win_window_info *window);
+win_monitor_info *winwindow_video_window_monitor(win_window_info *window, const RECT *proposed);
+
+LRESULT CALLBACK winwindow_video_window_proc(HWND wnd, UINT message, WPARAM wparam, LPARAM lparam);
+void winwindow_toggle_full_screen(void);
+
+void winwindow_process_events_periodic(void);
+void winwindow_process_events(int ingame);
+
+void winwindow_ui_pause_from_window_thread(int pause);
+void winwindow_ui_pause_from_main_thread(int pause);
+int winwindow_ui_is_paused(void);
+
+void winwindow_ui_exec_on_main_thread(void (*func)(void *), void *param);
+void winwindow_dispatch_message(MSG *message);
+
+#if HAS_WINDOW_MENU
+int win_create_menu(HMENU *menus);
+#endif
+
+
+
+//============================================================
+// win_has_menu
+//============================================================
+
+INLINE BOOL win_has_menu(win_window_info *window)
+{
+ return GetMenu(window->hwnd) ? TRUE : FALSE;
+}
+
+
+//============================================================
+// rect_width / rect_height
+//============================================================
+
+INLINE int rect_width(const RECT *rect)
+{
+ return rect->right - rect->left;
+}
+
+
+INLINE int rect_height(const RECT *rect)
+{
+ return rect->bottom - rect->top;
+}
+
+#endif
diff --git a/src/osd/windows/windows.mak b/src/osd/windows/windows.mak
new file mode 100644
index 00000000000..be5bc9c4948
--- /dev/null
+++ b/src/osd/windows/windows.mak
@@ -0,0 +1,299 @@
+###########################################################################
+#
+# windows.mak
+#
+# Windows-specific makefile
+#
+# Copyright (c) 1996-2007, Nicola Salmoria and the MAME Team.
+# Visit http://mamedev.org for licensing and usage restrictions.
+#
+###########################################################################
+
+
+###########################################################################
+################# BEGIN USER-CONFIGURABLE OPTIONS #####################
+###########################################################################
+
+
+#-------------------------------------------------
+# specify build options; see each option below
+# for details
+#-------------------------------------------------
+
+# uncomment next line to enable a build using Microsoft tools
+# MSVC_BUILD = 1
+
+# uncomment next line to use cygwin compiler
+# CYGWIN_BUILD = 1
+
+# uncomment next line to enable multi-monitor stubs on Windows 95/NT
+# you will need to find multimon.h and put it into your include
+# path in order to make this work
+# WIN95_MULTIMON = 1
+
+# uncomment next line to enable a Unicode build
+# UNICODE = 1
+
+
+
+###########################################################################
+################## END USER-CONFIGURABLE OPTIONS ######################
+###########################################################################
+
+
+#-------------------------------------------------
+# object and source roots
+#-------------------------------------------------
+
+WINSRC = $(SRC)/osd/$(OSD)
+WINOBJ = $(OBJ)/osd/$(OSD)
+
+OBJDIRS += $(WINOBJ)
+
+
+
+#-------------------------------------------------
+# configure the resource compiler
+#-------------------------------------------------
+
+RC = @windres --use-temp-file
+
+RCDEFS = -DNDEBUG -D_WIN32_IE=0x0400
+
+RCFLAGS = -O coff -I $(WINSRC) -I $(WINOBJ)
+
+
+
+#-------------------------------------------------
+# overrides for the CYGWIN compiler
+#-------------------------------------------------
+
+ifdef CYGWIN_BUILD
+CFLAGS += -mno-cygwin
+LDFLAGS += -mno-cygwin
+endif
+
+
+
+#-------------------------------------------------
+# overrides for the MSVC compiler
+#-------------------------------------------------
+
+ifdef MSVC_BUILD
+
+VCONV = $(WINOBJ)/vconv$(EXE)
+
+# replace the various compilers with vconv.exe prefixes
+CC = @$(VCONV) gcc -I.
+LD = @$(VCONV) ld /profile
+AR = @$(VCONV) ar
+RC = @$(VCONV) windres
+
+# make sure we use the multithreaded runtime
+ifdef DEBUG
+CC += /MTd
+else
+CC += /MT
+endif
+
+# turn on link-time codegen if the MAXOPT flag is also set
+ifdef MAXOPT
+CC += /GL
+LD += /LTCG
+endif
+
+ifdef PTR64
+CC += /wd4267 /Wp64
+endif
+
+# add some VC++-specific defines
+DEFS += -D_CRT_SECURE_NO_DEPRECATE -DXML_STATIC -D__inline__=__inline -Dsnprintf=_snprintf -Dvsnprintf=_vsnprintf
+
+# make msvcprep into a pre-build step
+# OSPREBUILD = $(VCONV)
+
+# add VCONV to the build tools
+BUILD += $(VCONV)
+
+$(VCONV): $(WINOBJ)/vconv.o
+ @echo Linking $@...
+ifdef PTR64
+ @link.exe /nologo $^ version.lib bufferoverflowu.lib /out:$@
+else
+ @link.exe /nologo $^ version.lib /out:$@
+endif
+
+$(WINOBJ)/vconv.o: $(WINSRC)/vconv.c
+ @echo Compiling $<...
+ @cl.exe /nologo /O1 -D_CRT_SECURE_NO_DEPRECATE -c $< /Fo$@
+
+endif
+
+
+
+#-------------------------------------------------
+# due to quirks of using /bin/sh, we need to
+# explicitly specify the current path
+#-------------------------------------------------
+
+CURPATH = ./
+
+
+
+#-------------------------------------------------
+# Windows-specific debug objects and flags
+#-------------------------------------------------
+
+# define the x64 ABI to be Windows
+DEFS += -DX64_WINDOWS_ABI
+
+# map all instances of "main" to "utf8_main"
+DEFS += -Dmain=utf8_main
+
+# debug build: enable guard pages on all memory allocations
+ifdef DEBUG
+ifndef WINUI
+DEFS += -DMALLOC_DEBUG
+LDFLAGS += -Wl,--allow-multiple-definition
+endif
+endif
+
+# enable UNICODE flags for unicode builds
+ifdef UNICODE
+DEFS += -DUNICODE -D_UNICODE
+endif
+
+
+
+#-------------------------------------------------
+# Windows-specific flags and libraries
+#-------------------------------------------------
+
+# add our prefix files to the mix
+CFLAGS += -include $(WINSRC)/winprefix.h
+
+ifdef WIN95_MULTIMON
+CFLAGS += -DWIN95_MULTIMON
+endif
+
+# add the windows libaries
+LIBS += -luser32 -lgdi32 -lddraw -ldsound -ldinput -ldxguid -lwinmm -ladvapi32 -lcomctl32 -lshlwapi
+
+ifdef PTR64
+LIBS += -lbufferoverflowu
+endif
+
+
+
+#-------------------------------------------------
+# OSD core library
+#-------------------------------------------------
+
+OSDCOREOBJS = \
+ $(WINOBJ)/main.o \
+ $(WINOBJ)/strconv.o \
+ $(WINOBJ)/windir.o \
+ $(WINOBJ)/winfile.o \
+ $(WINOBJ)/winmisc.o \
+ $(WINOBJ)/winsync.o \
+ $(WINOBJ)/wintime.o \
+ $(WINOBJ)/winutf8.o \
+ $(WINOBJ)/winutil.o \
+ $(WINOBJ)/winwork.o \
+
+# if malloc debugging is enabled, include the necessary code
+ifneq ($(findstring MALLOC_DEBUG,$(DEFS)),)
+OSDCOREOBJS += \
+ $(WINOBJ)/winalloc.o
+endif
+
+
+
+#-------------------------------------------------
+# OSD Windows library
+#-------------------------------------------------
+
+OSDOBJS = \
+ $(WINOBJ)/d3d8intf.o \
+ $(WINOBJ)/d3d9intf.o \
+ $(WINOBJ)/drawd3d.o \
+ $(WINOBJ)/drawdd.o \
+ $(WINOBJ)/drawgdi.o \
+ $(WINOBJ)/drawnone.o \
+ $(WINOBJ)/input.o \
+ $(WINOBJ)/output.o \
+ $(WINOBJ)/sound.o \
+ $(WINOBJ)/video.o \
+ $(WINOBJ)/window.o \
+ $(WINOBJ)/winmain.o
+
+# extra dependencies
+$(WINOBJ)/drawdd.o : $(SRC)/emu/rendersw.c
+$(WINOBJ)/drawgdi.o : $(SRC)/emu/rendersw.c
+
+# add debug-specific files
+ifdef DEBUG
+OSDOBJS += \
+ $(WINOBJ)/debugwin.o
+endif
+
+# add a stub resource file
+RESFILE = $(WINOBJ)/mame.res
+
+
+
+#-------------------------------------------------
+# rules for building the libaries
+#-------------------------------------------------
+
+$(LIBOCORE): $(OSDCOREOBJS)
+
+$(LIBOSD): $(OSDOBJS)
+
+
+
+#-------------------------------------------------
+# rule for making the ledutil sample
+#-------------------------------------------------
+
+ledutil$(EXE): $(WINOBJ)/ledutil.o $(LIBOCORE)
+ @echo Linking $@...
+ $(LD) $(LDFLAGS) $^ $(LIBS) -o $@
+
+TOOLS += ledutil$(EXE)
+
+
+
+#-------------------------------------------------
+# rule for making the verinfo tool
+#-------------------------------------------------
+
+VERINFO = $(WINOBJ)/verinfo$(EXE)
+
+$(VERINFO): $(WINOBJ)/verinfo.o $(LIBOCORE)
+ @echo Linking $@...
+ $(LD) $(LDFLAGS) $^ $(LIBS) -o $@
+
+BUILD += $(VERINFO)
+
+
+
+#-------------------------------------------------
+# generic rule for the resource compiler
+#-------------------------------------------------
+
+$(WINOBJ)/%.res: $(WINSRC)/%.rc | $(OSPREBUILD)
+ @echo Compiling resources $<...
+ $(RC) $(RCDEFS) $(RCFLAGS) -o $@ -i $<
+
+
+
+#-------------------------------------------------
+# rules for resource file
+#-------------------------------------------------
+
+$(RESFILE): $(WINSRC)/mame.rc $(WINOBJ)/mamevers.rc
+
+$(WINOBJ)/mamevers.rc: $(VERINFO) $(SRC)/version.c
+ @echo Emitting $@...
+ @$(VERINFO) $(SRC)/version.c > $@
diff --git a/src/osd/windows/winfile.c b/src/osd/windows/winfile.c
new file mode 100644
index 00000000000..6238e572cb2
--- /dev/null
+++ b/src/osd/windows/winfile.c
@@ -0,0 +1,331 @@
+//============================================================
+//
+// winfile.c - Win32 OSD core file access functions
+//
+// Copyright (c) 1996-2007, Nicola Salmoria and the MAME Team.
+// Visit http://mamedev.org for licensing and usage restrictions.
+//
+//============================================================
+
+// standard windows headers
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#include <winioctl.h>
+#include <tchar.h>
+
+// MAME headers
+#include "osdcore.h"
+
+// MAMEOS headers
+#include "strconv.h"
+#include "winutil.h"
+
+
+//============================================================
+// TYPE DEFINITIONS
+//============================================================
+
+struct _osd_file
+{
+ HANDLE handle;
+ TCHAR filename[1];
+};
+
+
+
+//============================================================
+// FUNCTION PROTOTYPES
+//============================================================
+
+static DWORD create_path_recursive(const TCHAR *path);
+
+
+
+//============================================================
+// osd_open
+//============================================================
+
+file_error osd_open(const char *path, UINT32 openflags, osd_file **file, UINT64 *filesize)
+{
+ DWORD disposition, access, sharemode;
+ file_error filerr = FILERR_NONE;
+ const TCHAR *src;
+ DWORD upper;
+ TCHAR *t_path;
+ TCHAR *dst;
+
+ // convert path to TCHAR
+ t_path = tstring_from_utf8(path);
+ if (t_path == NULL)
+ {
+ filerr = FILERR_OUT_OF_MEMORY;
+ goto error;
+ }
+
+ // allocate a file object, plus space for the converted filename
+ *file = malloc(sizeof(**file) + sizeof(TCHAR) * _tcslen(t_path));
+ if (*file == NULL)
+ {
+ filerr = FILERR_OUT_OF_MEMORY;
+ goto error;
+ }
+
+ // convert the path into something Windows compatible
+ dst = (*file)->filename;
+ for (src = t_path; *src != 0; src++)
+ *dst++ = *src;//(*src == '/') ? '\\' : *src;
+ *dst++ = 0;
+
+ // select the file open modes
+ if (openflags & OPEN_FLAG_WRITE)
+ {
+ disposition = (openflags & OPEN_FLAG_CREATE) ? CREATE_ALWAYS : OPEN_EXISTING;
+ access = (openflags & OPEN_FLAG_READ) ? (GENERIC_READ | GENERIC_WRITE) : GENERIC_WRITE;
+ sharemode = 0;
+ }
+ else if (openflags & OPEN_FLAG_READ)
+ {
+ disposition = OPEN_EXISTING;
+ access = GENERIC_READ;
+ sharemode = FILE_SHARE_READ;
+ }
+ else
+ {
+ filerr = FILERR_INVALID_ACCESS;
+ goto error;
+ }
+
+ // attempt to open the file
+ (*file)->handle = CreateFile((*file)->filename, access, sharemode, NULL, disposition, 0, NULL);
+ if ((*file)->handle == INVALID_HANDLE_VALUE)
+ {
+ DWORD error = GetLastError();
+
+ // create the path if necessary
+ if (error == ERROR_PATH_NOT_FOUND && (openflags & OPEN_FLAG_CREATE) && (openflags & OPEN_FLAG_CREATE_PATHS))
+ {
+ TCHAR *pathsep = _tcsrchr((*file)->filename, '\\');
+ if (pathsep != NULL)
+ {
+ // create the path up to the file
+ *pathsep = 0;
+ error = create_path_recursive((*file)->filename);
+ *pathsep = '\\';
+
+ // attempt to reopen the file
+ if (error == NO_ERROR)
+ (*file)->handle = CreateFile((*file)->filename, access, sharemode, NULL, disposition, 0, NULL);
+ }
+ }
+
+ // if we still failed, clean up and free
+ if ((*file)->handle == INVALID_HANDLE_VALUE)
+ {
+ filerr = win_error_to_file_error(error);
+ goto error;
+ }
+ }
+
+ // get the file size
+ *filesize = GetFileSize((*file)->handle, &upper);
+ *filesize |= (UINT64)upper << 32;
+
+error:
+ // cleanup
+ if (filerr != FILERR_NONE && *file != NULL)
+ {
+ free(*file);
+ *file = NULL;
+ }
+ free(t_path);
+ return filerr;
+}
+
+
+//============================================================
+// osd_read
+//============================================================
+
+file_error osd_read(osd_file *file, void *buffer, UINT64 offset, UINT32 length, UINT32 *actual)
+{
+ LONG upper = offset >> 32;
+ DWORD result;
+
+ // attempt to set the file pointer
+ result = SetFilePointer(file->handle, (UINT32)offset, &upper, FILE_BEGIN);
+ if (result == INVALID_SET_FILE_POINTER)
+ {
+ DWORD error = GetLastError();
+ if (error != NO_ERROR)
+ return win_error_to_file_error(error);
+ }
+
+ // then perform the read
+ if (!ReadFile(file->handle, buffer, length, &result, NULL))
+ return win_error_to_file_error(GetLastError());
+ if (actual != NULL)
+ *actual = result;
+ return FILERR_NONE;
+}
+
+
+//============================================================
+// osd_write
+//============================================================
+
+file_error osd_write(osd_file *file, const void *buffer, UINT64 offset, UINT32 length, UINT32 *actual)
+{
+ LONG upper = offset >> 32;
+ DWORD result;
+
+ // attempt to set the file pointer
+ result = SetFilePointer(file->handle, (UINT32)offset, &upper, FILE_BEGIN);
+ if (result == INVALID_SET_FILE_POINTER)
+ {
+ DWORD error = GetLastError();
+ if (error != NO_ERROR)
+ return win_error_to_file_error(error);
+ }
+
+ // then perform the read
+ if (!WriteFile(file->handle, buffer, length, &result, NULL))
+ return win_error_to_file_error(GetLastError());
+ if (actual != NULL)
+ *actual = result;
+ return FILERR_NONE;
+}
+
+
+//============================================================
+// osd_close
+//============================================================
+
+file_error osd_close(osd_file *file)
+{
+ // close the file handle and free the file structure
+ CloseHandle(file->handle);
+ free(file);
+ return FILERR_NONE;
+}
+
+
+//============================================================
+// osd_rmfile
+//============================================================
+
+file_error osd_rmfile(const char *filename)
+{
+ file_error filerr = FILERR_NONE;
+
+ TCHAR *tempstr = tstring_from_utf8(filename);
+ if (!tempstr)
+ {
+ filerr = FILERR_OUT_OF_MEMORY;
+ goto done;
+ }
+
+ if (!DeleteFile(tempstr))
+ {
+ filerr = win_error_to_file_error(GetLastError());
+ goto done;
+ }
+
+done:
+ if (tempstr)
+ free(tempstr);
+ return filerr;
+}
+
+
+//============================================================
+// osd_get_physical_drive_geometry
+//============================================================
+
+int osd_get_physical_drive_geometry(const char *filename, UINT32 *cylinders, UINT32 *heads, UINT32 *sectors, UINT32 *bps)
+{
+ DISK_GEOMETRY dg;
+ DWORD bytesRead;
+ TCHAR *t_filename;
+ HANDLE file;
+ int result;
+
+ // if it doesn't smell like a physical drive, just return FALSE
+ if (_strnicmp(filename, "\\\\.\\physicaldrive", 17) != 0)
+ return FALSE;
+
+ // do a create file on the drive
+ t_filename = tstring_from_utf8(filename);
+ if (t_filename == NULL)
+ return FALSE;
+ file = CreateFile(t_filename, GENERIC_READ, FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_FLAG_NO_BUFFERING, NULL);
+ free(t_filename);
+ if (file == INVALID_HANDLE_VALUE)
+ return FALSE;
+
+ // device I/O control should return the geometry
+ result = DeviceIoControl(file, IOCTL_DISK_GET_DRIVE_GEOMETRY, NULL, 0, &dg, sizeof(dg), &bytesRead, NULL);
+ CloseHandle(file);
+
+ // if that failed, return false
+ if (!result)
+ return FALSE;
+
+ // store the results
+ *cylinders = (UINT32)dg.Cylinders.QuadPart;
+ *heads = dg.TracksPerCylinder;
+ *sectors = dg.SectorsPerTrack;
+ *bps = dg.BytesPerSector;
+
+ // normalize
+ while (*heads > 16 && !(*heads & 1))
+ {
+ *heads /= 2;
+ *cylinders *= 2;
+ }
+ return TRUE;
+}
+
+
+//============================================================
+// osd_uchar_from_osdchar
+//============================================================
+
+int osd_uchar_from_osdchar(UINT32 *uchar, const char *osdchar, size_t count)
+{
+ WCHAR wch;
+
+ count = MIN(count, IsDBCSLeadByte(*osdchar) ? 2 : 1);
+ if (MultiByteToWideChar(CP_ACP, 0, osdchar, (DWORD)count, &wch, 1) != 0)
+ *uchar = wch;
+ else
+ *uchar = 0;
+ return (int) count;
+}
+
+
+//============================================================
+// create_path_recursive
+//============================================================
+
+DWORD create_path_recursive(const TCHAR *path)
+{
+ TCHAR *sep = _tcsrchr(path, '\\');
+ file_error filerr;
+
+ // if there's still a separator, and it's not the root, nuke it and recurse
+ if (sep != NULL && sep > path && sep[0] != ':' && sep[-1] != '\\')
+ {
+ *sep = 0;
+ filerr = create_path_recursive(path);
+ *sep = '\\';
+ }
+
+ // if the path already exists, we're done
+ if (GetFileAttributes(path) != INVALID_FILE_ATTRIBUTES)
+ return NO_ERROR;
+
+ // create the path
+ if (CreateDirectory(path, NULL) == 0)
+ return GetLastError();
+ return NO_ERROR;
+}
diff --git a/src/osd/windows/winmain.c b/src/osd/windows/winmain.c
new file mode 100644
index 00000000000..2879f328d3f
--- /dev/null
+++ b/src/osd/windows/winmain.c
@@ -0,0 +1,844 @@
+//============================================================
+//
+// winmain.c - Win32 main program
+//
+// Copyright (c) 1996-2007, Nicola Salmoria and the MAME Team.
+// Visit http://mamedev.org for licensing and usage restrictions.
+//
+//============================================================
+
+// standard windows headers
+#define _WIN32_WINNT 0x0501
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#include <commctrl.h>
+#include <mmsystem.h>
+#include <tchar.h>
+
+// standard C headers
+#include <ctype.h>
+#include <stdarg.h>
+
+// MAME headers
+#include "driver.h"
+#include "clifront.h"
+
+// MAMEOS headers
+#include "winmain.h"
+#include "window.h"
+#include "video.h"
+#include "sound.h"
+#include "input.h"
+#include "output.h"
+#include "config.h"
+#include "osdepend.h"
+#include "strconv.h"
+#include "winutf8.h"
+#include "winutil.h"
+
+#define ENABLE_PROFILER 0
+#define DEBUG_SLOW_LOCKS 0
+
+
+//============================================================
+// TYPE DEFINITIONS
+//============================================================
+
+#ifdef UNICODE
+#define UNICODE_POSTFIX "W"
+#else
+#define UNICODE_POSTFIX "A"
+#endif
+
+typedef BOOL (WINAPI *try_enter_critical_section_ptr)(LPCRITICAL_SECTION lpCriticalSection);
+
+typedef HANDLE (WINAPI *av_set_mm_thread_characteristics_ptr)(LPCTSTR TaskName, LPDWORD TaskIndex);
+typedef HANDLE (WINAPI *av_set_mm_max_thread_characteristics_ptr)(LPCTSTR FirstTask, LPCTSTR SecondTask, LPDWORD TaskIndex);
+typedef BOOL (WINAPI *av_revert_mm_thread_characteristics_ptr)(HANDLE AvrtHandle);
+
+
+
+//============================================================
+// GLOBAL VARIABLES
+//============================================================
+
+// this line prevents globbing on the command line
+int _CRT_glob = 0;
+
+
+
+//============================================================
+// LOCAL VARIABLES
+//============================================================
+
+static try_enter_critical_section_ptr try_enter_critical_section;
+
+static av_set_mm_thread_characteristics_ptr av_set_mm_thread_characteristics;
+static av_set_mm_max_thread_characteristics_ptr av_set_mm_max_thread_characteristics;
+static av_revert_mm_thread_characteristics_ptr av_revert_mm_thread_characteristics;
+
+static char mapfile_name[MAX_PATH];
+static LPTOP_LEVEL_EXCEPTION_FILTER pass_thru_filter;
+
+#ifndef MESS
+static const TCHAR helpfile[] = TEXT("docs\\windows.txt");
+#else
+static const TCHAR helpfile[] = TEXT("mess.chm");
+#endif
+
+//static HANDLE mm_task = NULL;
+//static DWORD task_index = 0;
+static int timeresult;
+//static MMRESULT result;
+static TIMECAPS caps;
+
+
+
+//============================================================
+// PROTOTYPES
+//============================================================
+
+static void osd_exit(running_machine *machine);
+
+static void soft_link_functions(void);
+static int is_double_click_start(int argc);
+static LONG CALLBACK exception_filter(struct _EXCEPTION_POINTERS *info);
+static const char *lookup_symbol(UINT32 address);
+static int get_code_base_size(UINT32 *base, UINT32 *size);
+
+static void start_profiler(void);
+static void stop_profiler(void);
+
+
+
+//============================================================
+// OPTIONS
+//============================================================
+
+// struct definitions
+const options_entry mame_win_options[] =
+{
+ // debugging options
+ { NULL, NULL, OPTION_HEADER, "WINDOWS DEBUGGING OPTIONS" },
+ { "oslog", "0", OPTION_BOOLEAN, "output error.log data to the system debugger" },
+
+ // performance options
+ { NULL, NULL, OPTION_HEADER, "WINDOWS PERFORMANCE OPTIONS" },
+ { "priority(-15-1)", "0", 0, "thread priority for the main game thread; range from -15 to 1" },
+ { "multithreading;mt", "0", OPTION_BOOLEAN, "enable multithreading; this enables rendering and blitting on a separate thread" },
+
+ // video options
+ { NULL, NULL, OPTION_HEADER, "WINDOWS VIDEO OPTIONS" },
+ { "video", "d3d", 0, "video output method: none, gdi, ddraw, or d3d" },
+ { "numscreens(1-4)", "1", 0, "number of screens to create; usually, you want just one" },
+ { "window;w", "0", OPTION_BOOLEAN, "enable window mode; otherwise, full screen mode is assumed" },
+ { "maximize;max", "1", OPTION_BOOLEAN, "default to maximized windows; otherwise, windows will be minimized" },
+ { "keepaspect;ka", "1", OPTION_BOOLEAN, "constrain to the proper aspect ratio" },
+ { "prescale", "1", 0, "scale screen rendering by this amount in software" },
+ { "effect", "none", 0, "name of a PNG file to use for visual effects, or 'none'" },
+ { "waitvsync", "0", OPTION_BOOLEAN, "enable waiting for the start of VBLANK before flipping screens; reduces tearing effects" },
+ { "syncrefresh", "0", OPTION_BOOLEAN, "enable using the start of VBLANK for throttling instead of the game time" },
+
+ // DirectDraw-specific options
+ { NULL, NULL, OPTION_HEADER, "DIRECTDRAW-SPECIFIC OPTIONS" },
+ { "hwstretch;hws", "1", OPTION_BOOLEAN, "enable hardware stretching" },
+
+ // Direct3D-specific options
+ { NULL, NULL, OPTION_HEADER, "DIRECT3D-SPECIFIC OPTIONS" },
+ { "d3dversion(8-9)", "9", 0, "specify the preferred Direct3D version (8 or 9)" },
+ { "filter;d3dfilter;flt", "1", OPTION_BOOLEAN, "enable bilinear filtering on screen output" },
+
+ // per-window options
+ { NULL, NULL, OPTION_HEADER, "PER-WINDOW VIDEO OPTIONS" },
+ { "screen", "auto", 0, "explicit name of all screens; 'auto' here will try to make a best guess" },
+ { "aspect;screen_aspect", "auto", 0, "aspect ratio for all screens; 'auto' here will try to make a best guess" },
+ { "resolution;r", "auto", 0, "preferred resolution for all screens; format is <width>x<height>[@<refreshrate>] or 'auto'" },
+ { "view", "auto", 0, "preferred view for all screens" },
+
+ { "screen0", "auto", 0, "explicit name of the first screen; 'auto' here will try to make a best guess" },
+ { "aspect0", "auto", 0, "aspect ratio of the first screen; 'auto' here will try to make a best guess" },
+ { "resolution0;r0", "auto", 0, "preferred resolution of the first screen; format is <width>x<height>[@<refreshrate>] or 'auto'" },
+ { "view0", "auto", 0, "preferred view for the first screen" },
+
+ { "screen1", "auto", 0, "explicit name of the second screen; 'auto' here will try to make a best guess" },
+ { "aspect1", "auto", 0, "aspect ratio of the second screen; 'auto' here will try to make a best guess" },
+ { "resolution1;r1", "auto", 0, "preferred resolution of the second screen; format is <width>x<height>[@<refreshrate>] or 'auto'" },
+ { "view1", "auto", 0, "preferred view for the second screen" },
+
+ { "screen2", "auto", 0, "explicit name of the third screen; 'auto' here will try to make a best guess" },
+ { "aspect2", "auto", 0, "aspect ratio of the third screen; 'auto' here will try to make a best guess" },
+ { "resolution2;r2", "auto", 0, "preferred resolution of the third screen; format is <width>x<height>[@<refreshrate>] or 'auto'" },
+ { "view2", "auto", 0, "preferred view for the third screen" },
+
+ { "screen3", "auto", 0, "explicit name of the fourth screen; 'auto' here will try to make a best guess" },
+ { "aspect3", "auto", 0, "aspect ratio of the fourth screen; 'auto' here will try to make a best guess" },
+ { "resolution3;r3", "auto", 0, "preferred resolution of the fourth screen; format is <width>x<height>[@<refreshrate>] or 'auto'" },
+ { "view3", "auto", 0, "preferred view for the fourth screen" },
+
+ // full screen options
+ { NULL, NULL, OPTION_HEADER, "FULL SCREEN OPTIONS" },
+ { "triplebuffer;tb", "0", OPTION_BOOLEAN, "enable triple buffering" },
+ { "switchres", "0", OPTION_BOOLEAN, "enable resolution switching" },
+ { "full_screen_brightness;fsb(0.1-2.0)","1.0", 0, "brightness value in full screen mode" },
+ { "full_screen_contrast;fsc(0.1-2.0)", "1.0", 0, "contrast value in full screen mode" },
+ { "full_screen_gamma;fsg(0.1-3.0)", "1.0", 0, "gamma value in full screen mode" },
+
+ // sound options
+ { NULL, NULL, OPTION_HEADER, "WINDOWS SOUND OPTIONS" },
+ { "audio_latency(1-5)", "2", 0, "set audio latency (increase to reduce glitches)" },
+
+ // input options
+ { NULL, NULL, OPTION_HEADER, "INPUT DEVICE OPTIONS" },
+ { "dual_lightgun;dual", "0", OPTION_BOOLEAN, "enable dual lightgun input" },
+
+ { NULL }
+};
+
+
+//============================================================
+// winui_output_error
+//============================================================
+
+static void winui_output_error(void *param, const char *format, va_list argptr)
+{
+ char buffer[1024];
+
+ // if we are in fullscreen mode, go to windowed mode
+ if ((video_config.windowed == 0) && (win_window_list != NULL))
+ winwindow_toggle_full_screen();
+
+ vsnprintf(buffer, ARRAY_LENGTH(buffer), format, argptr);
+ win_message_box_utf8(win_window_list ? win_window_list->hwnd : NULL, buffer, APPNAME, MB_OK);
+}
+
+
+
+//============================================================
+// utf8_main
+//============================================================
+
+int utf8_main(int argc, char **argv)
+{
+ char *ext;
+
+ // initialize common controls
+ InitCommonControls();
+
+ // set up exception handling
+ pass_thru_filter = SetUnhandledExceptionFilter(exception_filter);
+
+ // if we're a GUI app, out errors to message boxes
+ if (win_is_gui_application() || is_double_click_start(argc))
+ {
+ // if we are a GUI app, output errors to message boxes
+ mame_set_output_channel(OUTPUT_CHANNEL_ERROR, winui_output_error, NULL, NULL, NULL);
+
+ // make sure any console window that opened on our behalf is nuked
+ FreeConsole();
+ }
+
+ // soft-link optional functions
+ soft_link_functions();
+
+ strcpy(mapfile_name, argv[0]);
+ ext = strchr(mapfile_name, '.');
+ if (ext)
+ strcpy(ext, ".map");
+ else
+ strcat(mapfile_name, ".map");
+
+ // parse config and cmdline options
+ return cli_execute(argc, argv, mame_win_options);
+}
+
+
+//============================================================
+// output_oslog
+//============================================================
+
+static void output_oslog(running_machine *machine, const char *buffer)
+{
+ win_output_debug_string_utf8(buffer);
+}
+
+
+//============================================================
+// osd_init
+//============================================================
+
+void osd_init(running_machine *machine)
+{
+ // thread priority
+ if (!options_get_bool(mame_options(), OPTION_DEBUG))
+ SetThreadPriority(GetCurrentThread(), options_get_int(mame_options(), WINOPTION_PRIORITY));
+
+ // ensure we get called on the way out
+ add_exit_callback(machine, osd_exit);
+
+ // initialize the subsystems
+ winvideo_init(machine);
+ winsound_init(machine);
+ wininput_init(machine);
+ winoutput_init(machine);
+
+ // hook up the debugger log
+ if (options_get_bool(mame_options(), WINOPTION_OSLOG))
+ add_logerror_callback(machine, output_oslog);
+
+ // crank up the multimedia timer resolution to its max
+ // this gives the system much finer timeslices
+ timeresult = timeGetDevCaps(&caps, sizeof(caps));
+ if (timeresult == TIMERR_NOERROR)
+ timeBeginPeriod(caps.wPeriodMin);
+
+ // set our multimedia tasks if we can
+// if (av_set_mm_thread_characteristics != NULL)
+// mm_task = (*av_set_mm_thread_characteristics)(TEXT("Playback"), &task_index);
+
+ start_profiler();
+}
+
+
+static void osd_exit(running_machine *machine)
+{
+ stop_profiler();
+
+ // turn off our multimedia tasks
+// if (av_revert_mm_thread_characteristics != NULL)
+// (*av_revert_mm_thread_characteristics)(mm_task);
+
+ // restore the timer resolution
+ if (timeresult == TIMERR_NOERROR)
+ timeEndPeriod(caps.wPeriodMin);
+
+ // one last pass at events
+ winwindow_process_events(0);
+}
+
+
+
+//============================================================
+// soft_link_functions
+//============================================================
+
+static void soft_link_functions(void)
+{
+ HMODULE library;
+
+ // see if we can use TryEnterCriticalSection
+ try_enter_critical_section = NULL;
+ library = LoadLibrary(TEXT("kernel32.dll"));
+ if (library != NULL)
+ try_enter_critical_section = (try_enter_critical_section_ptr)GetProcAddress(library, "TryEnterCriticalSection");
+
+ // see if we can use the multimedia scheduling functions in Vista
+ av_set_mm_thread_characteristics = NULL;
+ av_set_mm_max_thread_characteristics = NULL;
+ av_revert_mm_thread_characteristics = NULL;
+ library = LoadLibrary(TEXT("avrt.dll"));
+ if (library != NULL)
+ {
+ av_set_mm_thread_characteristics = (av_set_mm_thread_characteristics_ptr)GetProcAddress(library, "AvSetMmThreadCharacteristics" UNICODE_POSTFIX);
+ av_set_mm_max_thread_characteristics = (av_set_mm_max_thread_characteristics_ptr)GetProcAddress(library, "AvSetMmMaxThreadCharacteristics" UNICODE_POSTFIX);
+ av_revert_mm_thread_characteristics = (av_revert_mm_thread_characteristics_ptr)GetProcAddress(library, "AvRevertMmThreadCharacteristics");
+ }
+}
+
+
+//============================================================
+// check_for_double_click_start
+//============================================================
+
+static int is_double_click_start(int argc)
+{
+ STARTUPINFO startup_info = { sizeof(STARTUPINFO) };
+
+ // determine our startup information
+ GetStartupInfo(&startup_info);
+
+ // try to determine if MAME was simply double-clicked
+ return (argc <= 1 && startup_info.dwFlags && !(startup_info.dwFlags & STARTF_USESTDHANDLES));
+}
+
+
+//============================================================
+// exception_filter
+//============================================================
+
+static LONG CALLBACK exception_filter(struct _EXCEPTION_POINTERS *info)
+{
+ static const struct
+ {
+ DWORD code;
+ const char *string;
+ } exception_table[] =
+ {
+ { EXCEPTION_ACCESS_VIOLATION, "ACCESS VIOLATION" },
+ { EXCEPTION_DATATYPE_MISALIGNMENT, "DATATYPE MISALIGNMENT" },
+ { EXCEPTION_BREAKPOINT, "BREAKPOINT" },
+ { EXCEPTION_SINGLE_STEP, "SINGLE STEP" },
+ { EXCEPTION_ARRAY_BOUNDS_EXCEEDED, "ARRAY BOUNDS EXCEEDED" },
+ { EXCEPTION_FLT_DENORMAL_OPERAND, "FLOAT DENORMAL OPERAND" },
+ { EXCEPTION_FLT_DIVIDE_BY_ZERO, "FLOAT DIVIDE BY ZERO" },
+ { EXCEPTION_FLT_INEXACT_RESULT, "FLOAT INEXACT RESULT" },
+ { EXCEPTION_FLT_INVALID_OPERATION, "FLOAT INVALID OPERATION" },
+ { EXCEPTION_FLT_OVERFLOW, "FLOAT OVERFLOW" },
+ { EXCEPTION_FLT_STACK_CHECK, "FLOAT STACK CHECK" },
+ { EXCEPTION_FLT_UNDERFLOW, "FLOAT UNDERFLOW" },
+ { EXCEPTION_INT_DIVIDE_BY_ZERO, "INTEGER DIVIDE BY ZERO" },
+ { EXCEPTION_INT_OVERFLOW, "INTEGER OVERFLOW" },
+ { EXCEPTION_PRIV_INSTRUCTION, "PRIVILEGED INSTRUCTION" },
+ { EXCEPTION_IN_PAGE_ERROR, "IN PAGE ERROR" },
+ { EXCEPTION_ILLEGAL_INSTRUCTION, "ILLEGAL INSTRUCTION" },
+ { EXCEPTION_NONCONTINUABLE_EXCEPTION,"NONCONTINUABLE EXCEPTION" },
+ { EXCEPTION_STACK_OVERFLOW, "STACK OVERFLOW" },
+ { EXCEPTION_INVALID_DISPOSITION, "INVALID DISPOSITION" },
+ { EXCEPTION_GUARD_PAGE, "GUARD PAGE VIOLATION" },
+ { EXCEPTION_INVALID_HANDLE, "INVALID HANDLE" },
+ { 0, "UNKNOWN EXCEPTION" }
+ };
+ static int already_hit = 0;
+#ifndef PTR64
+ UINT32 code_start, code_size;
+#endif
+ int i;
+
+ // if we're hitting this recursively, just exit
+ if (already_hit)
+ ExitProcess(100);
+ already_hit = 1;
+
+#ifdef MAME_DEBUG
+{
+extern void debug_flush_traces(void);
+debug_flush_traces();
+}
+#endif
+
+ // find our man
+ for (i = 0; exception_table[i].code != 0; i++)
+ if (info->ExceptionRecord->ExceptionCode == exception_table[i].code)
+ break;
+
+ // print the exception type and address
+ fprintf(stderr, "\n-----------------------------------------------------\n");
+ fprintf(stderr, "Exception at EIP=%p%s: %s\n", info->ExceptionRecord->ExceptionAddress,
+ lookup_symbol((FPTR)info->ExceptionRecord->ExceptionAddress), exception_table[i].string);
+
+ // for access violations, print more info
+ if (info->ExceptionRecord->ExceptionCode == EXCEPTION_ACCESS_VIOLATION)
+ fprintf(stderr, "While attempting to %s memory at %08X\n",
+ info->ExceptionRecord->ExceptionInformation[0] ? "write" : "read",
+ (UINT32)info->ExceptionRecord->ExceptionInformation[1]);
+
+ // print the state of the CPU
+ fprintf(stderr, "-----------------------------------------------------\n");
+#ifdef PTR64
+ fprintf(stderr, "RAX=%p RBX=%p RCX=%p RDX=%p\n",
+ (void *)info->ContextRecord->Rax,
+ (void *)info->ContextRecord->Rbx,
+ (void *)info->ContextRecord->Rcx,
+ (void *)info->ContextRecord->Rdx);
+ fprintf(stderr, "RSI=%p RDI=%p RBP=%p RSP=%p\n",
+ (void *)info->ContextRecord->Rsi,
+ (void *)info->ContextRecord->Rdi,
+ (void *)info->ContextRecord->Rbp,
+ (void *)info->ContextRecord->Rsp);
+ fprintf(stderr, " R8=%p R9=%p R10=%p R11=%p\n",
+ (void *)info->ContextRecord->R8,
+ (void *)info->ContextRecord->R9,
+ (void *)info->ContextRecord->R10,
+ (void *)info->ContextRecord->R11);
+ fprintf(stderr, "R12=%p R13=%p R14=%p R15=%p\n",
+ (void *)info->ContextRecord->R12,
+ (void *)info->ContextRecord->R13,
+ (void *)info->ContextRecord->R14,
+ (void *)info->ContextRecord->R15);
+#else
+ fprintf(stderr, "EAX=%p EBX=%p ECX=%p EDX=%p\n",
+ (void *)info->ContextRecord->Eax,
+ (void *)info->ContextRecord->Ebx,
+ (void *)info->ContextRecord->Ecx,
+ (void *)info->ContextRecord->Edx);
+ fprintf(stderr, "ESI=%p EDI=%p EBP=%p ESP=%p\n",
+ (void *)info->ContextRecord->Esi,
+ (void *)info->ContextRecord->Edi,
+ (void *)info->ContextRecord->Ebp,
+ (void *)info->ContextRecord->Esp);
+#endif
+
+#ifndef PTR64
+ // crawl the stack for a while
+ if (get_code_base_size(&code_start, &code_size))
+ {
+ char prev_symbol[1024], curr_symbol[1024];
+ UINT32 last_call = (UINT32)info->ExceptionRecord->ExceptionAddress;
+ UINT32 esp_start = info->ContextRecord->Esp;
+ UINT32 esp_end = (esp_start | 0xffff) + 1;
+ UINT32 esp;
+
+ // reprint the actual exception address
+ fprintf(stderr, "-----------------------------------------------------\n");
+ fprintf(stderr, "Stack crawl:\n");
+ fprintf(stderr, "exception-> %08X%s\n", last_call, strcpy(prev_symbol, lookup_symbol(last_call)));
+
+ // crawl the stack until we hit the next 64k boundary
+ for (esp = esp_start; esp < esp_end; esp += 4)
+ {
+ UINT32 stack_val = *(UINT32 *)esp;
+
+ // if the value on the stack points within the code block, check it out
+ if (stack_val >= code_start && stack_val < code_start + code_size)
+ {
+ UINT8 *return_addr = (UINT8 *)stack_val;
+ UINT32 call_target = 0;
+
+ // make sure the code that we think got us here is actually a CALL instruction
+ if (return_addr[-5] == 0xe8)
+ call_target = stack_val - 5 + *(INT32 *)&return_addr[-4];
+ if ((return_addr[-2] == 0xff && (return_addr[-1] & 0x38) == 0x10) ||
+ (return_addr[-3] == 0xff && (return_addr[-2] & 0x38) == 0x10) ||
+ (return_addr[-4] == 0xff && (return_addr[-3] & 0x38) == 0x10) ||
+ (return_addr[-5] == 0xff && (return_addr[-4] & 0x38) == 0x10) ||
+ (return_addr[-6] == 0xff && (return_addr[-5] & 0x38) == 0x10) ||
+ (return_addr[-7] == 0xff && (return_addr[-6] & 0x38) == 0x10))
+ call_target = 1;
+
+ // make sure it points somewhere a little before the last call
+ if (call_target == 1 || (call_target < last_call && call_target >= last_call - 0x1000))
+ {
+ char *stop_compare = strchr(prev_symbol, '+');
+
+ // don't print duplicate hits in the same routine
+ strcpy(curr_symbol, lookup_symbol(stack_val));
+ if (stop_compare == NULL || strncmp(curr_symbol, prev_symbol, stop_compare - prev_symbol))
+ {
+ strcpy(prev_symbol, curr_symbol);
+ fprintf(stderr, " %08X: %08X%s\n", esp, stack_val, curr_symbol);
+ last_call = stack_val;
+ }
+ }
+ }
+ }
+ }
+#endif
+
+ // exit
+ ExitProcess(100);
+}
+
+
+
+//============================================================
+// lookup_symbol
+//============================================================
+
+static const char *lookup_symbol(UINT32 address)
+{
+ static char buffer[1024];
+ FILE * map = fopen(mapfile_name, "r");
+ char symbol[1024], best_symbol[1024];
+ UINT32 addr, best_addr = 0;
+ char line[1024];
+
+ // if no file, return nothing
+ if (map == NULL)
+ return "";
+
+ // reset the bests
+ *best_symbol = 0;
+ best_addr = 0;
+
+ // parse the file, looking for map entries
+ while (fgets(line, sizeof(line) - 1, map))
+ if (!strncmp(line, " 0x", 18))
+ if (sscanf(line, " 0x%08x %s", &addr, symbol) == 2)
+ if (addr <= address && addr > best_addr)
+ {
+ best_addr = addr;
+ strcpy(best_symbol, symbol);
+ }
+
+ // create the final result
+ if (address - best_addr > 0x10000)
+ return "";
+ sprintf(buffer, " (%s+0x%04x)", best_symbol, address - best_addr);
+ return buffer;
+}
+
+
+
+//============================================================
+// get_code_base_size
+//============================================================
+
+static int get_code_base_size(UINT32 *base, UINT32 *size)
+{
+ FILE * map = fopen(mapfile_name, "r");
+ char line[1024];
+
+ // if no file, return nothing
+ if (map == NULL)
+ return 0;
+
+ // parse the file, looking for .text entry
+ while (fgets(line, sizeof(line) - 1, map))
+ if (!strncmp(line, ".text 0x", 18))
+ if (sscanf(line, ".text 0x%08x 0x%x", base, size) == 2)
+ return 1;
+
+ return 0;
+}
+
+
+#if ENABLE_PROFILER
+
+//============================================================
+//
+// profiler.c - Sampling profiler
+//
+//============================================================
+
+//============================================================
+// TYPE DEFINITIONS
+//============================================================
+
+#define MAX_SYMBOLS 65536
+
+typedef struct _map_entry map_entry;
+
+struct _map_entry
+{
+ UINT32 start;
+ UINT32 end;
+ UINT32 hits;
+ char *name;
+};
+
+//============================================================
+// LOCAL VARIABLES
+//============================================================
+
+static map_entry symbol_map[MAX_SYMBOLS];
+static int map_entries;
+
+static HANDLE profiler_thread;
+static DWORD profiler_thread_id;
+static volatile UINT8 profiler_thread_exit;
+
+//============================================================
+// compare_base
+// compare_hits -- qsort callbacks to sort on
+//============================================================
+
+static int CLIB_DECL compare_start(const void *item1, const void *item2)
+{
+ return ((const map_entry *)item1)->start - ((const map_entry *)item2)->start;
+}
+
+static int CLIB_DECL compare_hits(const void *item1, const void *item2)
+{
+ return ((const map_entry *)item2)->hits - ((const map_entry *)item1)->hits;
+}
+
+
+//============================================================
+// add_symbol_map_entry
+// parse_map_file
+//============================================================
+
+static void add_symbol_map_entry(UINT32 start, const char *name)
+{
+ if (map_entries == MAX_SYMBOLS)
+ fatalerror("Symbol table full");
+ symbol_map[map_entries].start = start;
+ symbol_map[map_entries].name = core_strdup(name);
+ if (symbol_map[map_entries].name == NULL)
+ fatalerror("Out of memory");
+ map_entries++;
+}
+
+static void parse_map_file(void)
+{
+ int got_text = 0;
+ char line[1024];
+ FILE *map;
+ int i;
+
+ // open the map file
+ map = fopen(mapfile_name, "r");
+ if (!map)
+ return;
+
+ // parse out the various symbols into map entries
+ map_entries = 0;
+ while (fgets(line, sizeof(line) - 1, map))
+ {
+ /* look for the code boundaries */
+ if (!got_text && !strncmp(line, ".text 0x", 18))
+ {
+ UINT32 base, size;
+ if (sscanf(line, ".text 0x%08x 0x%x", &base, &size) == 2)
+ {
+ add_symbol_map_entry(base, "Code start");
+ add_symbol_map_entry(base+size, "Other");
+ got_text = 1;
+ }
+ }
+
+ /* look for symbols */
+ else if (!strncmp(line, " 0x", 18))
+ {
+ char symbol[1024];
+ UINT32 addr;
+ if (sscanf(line, " 0x%08x %s", &addr, symbol) == 2)
+ {
+ add_symbol_map_entry(addr, symbol);
+ }
+ }
+ }
+
+ /* add a symbol for end-of-memory */
+ add_symbol_map_entry(~0, "<end>");
+ map_entries--;
+
+ /* close the file */
+ fclose(map);
+
+ /* sort by address */
+ qsort(symbol_map, map_entries, sizeof(symbol_map[0]), compare_start);
+
+ /* fill in the end of each bucket */
+ for (i = 0; i < map_entries; i++)
+ symbol_map[i].end = symbol_map[i+1].start ? (symbol_map[i+1].start - 1) : 0;
+}
+
+
+//============================================================
+// free_symbol_map
+//============================================================
+
+static void free_symbol_map(void)
+{
+ int i;
+
+ for (i = 0; i <= map_entries; i++)
+ {
+ free(symbol_map[i].name);
+ symbol_map[i].name = NULL;
+ }
+}
+
+
+//============================================================
+// output_symbol_list
+//============================================================
+
+static void output_symbol_list(FILE *f)
+{
+ map_entry *entry;
+ int i;
+
+ /* sort by hits */
+ qsort(symbol_map, map_entries, sizeof(symbol_map[0]), compare_hits);
+
+ for (i = 0, entry = symbol_map; i < map_entries; i++, entry++)
+ if (entry->hits > 0)
+ fprintf(f, "%10d %08X-%08X %s\n", entry->hits, entry->start, entry->end, entry->name);
+}
+
+
+
+//============================================================
+// increment_bucket
+//============================================================
+
+static void increment_bucket(UINT32 addr)
+{
+ int i;
+
+ for (i = 0; i < map_entries; i++)
+ if (addr <= symbol_map[i].end)
+ {
+ symbol_map[i].hits++;
+ return;
+ }
+}
+
+
+
+//============================================================
+// profiler_thread
+//============================================================
+
+static DWORD WINAPI profiler_thread_entry(LPVOID lpParameter)
+{
+ HANDLE mainThread = (HANDLE)lpParameter;
+ CONTEXT context;
+
+ /* loop until done */
+ memset(&context, 0, sizeof(context));
+ while (!profiler_thread_exit)
+ {
+ /* pause the main thread and get its context */
+ SuspendThread(mainThread);
+ context.ContextFlags = CONTEXT_FULL;
+ GetThreadContext(mainThread, &context);
+ ResumeThread(mainThread);
+
+ /* add to the bucket */
+ increment_bucket(context.Eip);
+
+ /* sleep */
+ Sleep(1);
+ }
+
+ return 0;
+}
+
+
+
+//============================================================
+// start_profiler
+//============================================================
+
+static void start_profiler(void)
+{
+ HANDLE currentThread;
+ BOOL result;
+
+ // parse the map file, if present
+ parse_map_file();
+
+ /* do the dance to get a handle to ourself */
+ result = DuplicateHandle(GetCurrentProcess(), GetCurrentThread(), GetCurrentProcess(), &currentThread,
+ THREAD_GET_CONTEXT | THREAD_SUSPEND_RESUME | THREAD_QUERY_INFORMATION, FALSE, 0);
+ assert_always(result, "Failed to get thread handle for main thread");
+
+ profiler_thread_exit = 0;
+
+ /* start the thread */
+ profiler_thread = CreateThread(NULL, 0, profiler_thread_entry, (LPVOID)currentThread, 0, &profiler_thread_id);
+ assert_always(profiler_thread, "Failed to create profiler thread\n");
+
+ /* max out the priority */
+ SetThreadPriority(profiler_thread, THREAD_PRIORITY_TIME_CRITICAL);
+}
+
+
+
+//============================================================
+// stop_profiler
+//============================================================
+
+static void stop_profiler(void)
+{
+ profiler_thread_exit = 1;
+ WaitForSingleObject(profiler_thread, 2000);
+ output_symbol_list(stderr);
+ free_symbol_map();
+}
+#else
+static void start_profiler(void) {}
+static void stop_profiler(void) {}
+#endif
diff --git a/src/osd/windows/winmain.h b/src/osd/windows/winmain.h
new file mode 100644
index 00000000000..5bc3a100f63
--- /dev/null
+++ b/src/osd/windows/winmain.h
@@ -0,0 +1,68 @@
+//============================================================
+//
+// winmain.h - Win32 main program and core headers
+//
+// Copyright (c) 1996-2007, Nicola Salmoria and the MAME Team.
+// Visit http://mamedev.org for licensing and usage restrictions.
+//
+//============================================================
+
+
+//============================================================
+// CONSTANTS
+//============================================================
+
+// video options
+#define WINOPTION_D3DVERSION "d3dversion"
+#define WINOPTION_VIDEO "video"
+#define WINOPTION_PRESCALE "prescale"
+#define WINOPTION_NUMSCREENS "numscreens"
+#define WINOPTION_WAITVSYNC "waitvsync"
+#define WINOPTION_TRIPLEBUFFER "triplebuffer"
+#define WINOPTION_HWSTRETCH "hwstretch"
+#define WINOPTION_SWITCHRES "switchres"
+#define WINOPTION_KEEPASPECT "keepaspect"
+#define WINOPTION_SYNCREFRESH "syncrefresh"
+#define WINOPTION_FULLSCREENGAMMA "full_screen_gamma"
+#define WINOPTION_FULLSCREENBRIGHTNESS "full_screen_brightness"
+#define WINOPTION_FULLLSCREENCONTRAST "full_screen_contrast"
+#define WINOPTION_EFFECT "effect"
+#define WINOPTION_ASPECT "aspect"
+#define WINOPTION_RESOLUTION "resolution"
+#define WINOPTION_RESOLUTION0 "resolution0"
+#define WINOPTION_RESOLUTION1 "resolution1"
+#define WINOPTION_RESOLUTION2 "resolution2"
+#define WINOPTION_RESOLUTION3 "resolution3"
+#define WINOPTION_FILTER "filter"
+#define WINOPTION_SCREEN "screen"
+
+// window options
+#define WINOPTION_VIEW "view"
+#define WINOPTION_MAXIMIZE "maximize"
+#define WINOPTION_WINDOW "window"
+
+// input options
+#define WINOPTION_DUAL_LIGHTGUN "dual_lightgun"
+
+// misc options
+#define WINOPTION_AUDIO_LATENCY "audio_latency"
+#define WINOPTION_PRIORITY "priority"
+#define WINOPTION_MULTITHREADING "multithreading"
+#define WINOPTION_OSLOG "oslog"
+
+
+
+//============================================================
+// GLOBAL VARIABLES
+//============================================================
+
+extern const options_entry mame_win_options[];
+
+
+
+//============================================================
+// FUNCTION PROTOTYPES
+//============================================================
+
+// use if you want to print something with the verbose flag
+void CLIB_DECL mame_printf_verbose(const char *text, ...) ATTR_PRINTF(1,2);
diff --git a/src/osd/windows/winmisc.c b/src/osd/windows/winmisc.c
new file mode 100644
index 00000000000..486066c864b
--- /dev/null
+++ b/src/osd/windows/winmisc.c
@@ -0,0 +1,65 @@
+//============================================================
+//
+// winmisc.c - Win32 OSD core miscellaneous functions
+//
+// Copyright (c) 1996-2007, Nicola Salmoria and the MAME Team.
+// Visit http://mamedev.org for licensing and usage restrictions.
+//
+//============================================================
+
+// standard windows headers
+#define _WIN32_WINNT 0x0400
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#include <tchar.h>
+
+// MAME headers
+#include "osdcore.h"
+
+// MAMEOS headers
+#include "winutf8.h"
+#include "strconv.h"
+
+
+//============================================================
+// osd_alloc_executable
+//============================================================
+
+void *osd_alloc_executable(size_t size)
+{
+ return VirtualAlloc(NULL, size, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
+}
+
+
+//============================================================
+// osd_free_executable
+//============================================================
+
+void osd_free_executable(void *ptr, size_t size)
+{
+ VirtualFree(ptr, 0, MEM_RELEASE);
+}
+
+
+//============================================================
+// osd_is_bad_read_ptr
+//============================================================
+
+int osd_is_bad_read_ptr(const void *ptr, size_t size)
+{
+ return IsBadReadPtr(ptr, size);
+}
+
+
+//============================================================
+// osd_break_into_debugger
+//============================================================
+
+void osd_break_into_debugger(const char *message)
+{
+ if (IsDebuggerPresent())
+ {
+ win_output_debug_string_utf8(message);
+ DebugBreak();
+ }
+}
diff --git a/src/osd/windows/winprefix.h b/src/osd/windows/winprefix.h
new file mode 100644
index 00000000000..a902120f2e7
--- /dev/null
+++ b/src/osd/windows/winprefix.h
@@ -0,0 +1,35 @@
+//============================================================
+//
+// winprefix.h - Win32 prefix file, included by ALL files
+//
+// Copyright (c) 1996-2007, Nicola Salmoria and the MAME Team.
+// Visit http://mamedev.org for licensing and usage restrictions.
+//
+//============================================================
+
+#ifdef MALLOC_DEBUG
+#include <stdlib.h>
+
+// override malloc to track file/line
+void* malloc_file_line(size_t size, const char *file, int line);
+void* calloc_file_line(size_t size, size_t count, const char *FILE, int line);
+void * realloc_file_line(void *memory, size_t size, const char *file, int line);
+
+#undef malloc
+#define malloc(x) malloc_file_line(x, __FILE__, __LINE__)
+#undef calloc
+#define calloc(x,y) calloc_file_line(x, y, __FILE__, __LINE__)
+#undef realloc
+#define realloc(x,y) realloc_file_line(x, y, __FILE__, __LINE__)
+#endif
+
+#ifdef _MSC_VER
+void *__cdecl _alloca(size_t);
+#define alloca _alloca
+#endif
+
+#ifdef __GNUC__
+#define alloca __builtin_alloca
+#endif
+
+#define PATH_SEPARATOR "\\"
diff --git a/src/osd/windows/winsync.c b/src/osd/windows/winsync.c
new file mode 100644
index 00000000000..4d86d2ef061
--- /dev/null
+++ b/src/osd/windows/winsync.c
@@ -0,0 +1,157 @@
+//============================================================
+//
+// winsync.c - Win32 OSD core synchronization functions
+//
+// Copyright (c) 1996-2007, Nicola Salmoria and the MAME Team.
+// Visit http://mamedev.org for licensing and usage restrictions.
+//
+//============================================================
+
+// standard windows headers
+#ifndef _WIN32_WINNT
+#define _WIN32_WINNT 0x0400
+#endif
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+
+// MAME headers
+#include "osdcore.h"
+
+
+//============================================================
+// DEBUGGING
+//============================================================
+
+#define DEBUG_SLOW_LOCKS 0
+
+
+
+//============================================================
+// TYPE DEFINITIONS
+//============================================================
+
+typedef BOOL (WINAPI *try_enter_critical_section_ptr)(LPCRITICAL_SECTION lpCriticalSection);
+
+struct _osd_lock
+{
+ CRITICAL_SECTION critsect;
+};
+
+
+
+//============================================================
+// GLOBAL VARIABLES
+//============================================================
+
+static try_enter_critical_section_ptr try_enter_critical_section = NULL;
+static int checked_for_try_enter = FALSE;
+
+
+
+//============================================================
+// osd_lock_alloc
+//============================================================
+
+osd_lock *osd_lock_alloc(void)
+{
+ osd_lock *lock = malloc(sizeof(*lock));
+ if (lock == NULL)
+ return NULL;
+ InitializeCriticalSection(&lock->critsect);
+ return lock;
+}
+
+
+//============================================================
+// osd_lock_acquire
+//============================================================
+
+void osd_lock_acquire(osd_lock *lock)
+{
+#if DEBUG_SLOW_LOCKS
+ osd_ticks_t ticks = osd_ticks();
+#endif
+
+ // block until we can acquire the lock
+ EnterCriticalSection(&lock->critsect);
+
+#if DEBUG_SLOW_LOCKS
+ // log any locks that take more than 1ms
+ ticks = osd_ticks() - ticks;
+ if (ticks > osd_ticks_per_second() / 1000) mame_printf_debug("Blocked %d ticks on lock acquire\n", (int)ticks);
+#endif
+}
+
+
+//============================================================
+// osd_lock_try
+//============================================================
+
+int osd_lock_try(osd_lock *lock)
+{
+ int result = TRUE;
+
+ // if we haven't yet checked for the TryEnter API, do it now
+ if (!checked_for_try_enter)
+ {
+ // see if we can use TryEnterCriticalSection
+ HMODULE library = LoadLibrary(TEXT("kernel32.dll"));
+ if (library != NULL)
+ try_enter_critical_section = (try_enter_critical_section_ptr)GetProcAddress(library, "TryEnterCriticalSection");
+ checked_for_try_enter = TRUE;
+ }
+
+ // if we have it, use it, otherwise just block
+ if (try_enter_critical_section != NULL)
+ result = (*try_enter_critical_section)(&lock->critsect);
+ else
+ EnterCriticalSection(&lock->critsect);
+ return result;
+}
+
+
+//============================================================
+// osd_lock_release
+//============================================================
+
+void osd_lock_release(osd_lock *lock)
+{
+ LeaveCriticalSection(&lock->critsect);
+}
+
+
+//============================================================
+// osd_lock_free
+//============================================================
+
+void osd_lock_free(osd_lock *lock)
+{
+ DeleteCriticalSection(&lock->critsect);
+ free(lock);
+}
+
+
+//============================================================
+// osd_compare_exchange32
+//============================================================
+
+#ifndef osd_compare_exchange32
+INT32 osd_compare_exchange32(INT32 volatile *ptr, INT32 compare, INT32 exchange)
+{
+ return InterlockedCompareExchange((LPLONG)ptr, (LONG)exchange, (LONG)compare);
+}
+#endif
+
+
+//============================================================
+// osd_compare_exchange64
+//============================================================
+
+#ifdef PTR64
+#ifndef osd_compare_exchange64
+INT64 osd_compare_exchange64(INT64 volatile *ptr, INT64 compare, INT64 exchange)
+{
+ return InterlockedCompareExchange64((LONGLONG*)ptr, (LONGLONG)exchange, (LONGLONG)compare);
+}
+#endif
+#endif
diff --git a/src/osd/windows/wintime.c b/src/osd/windows/wintime.c
new file mode 100644
index 00000000000..4a92b237ba6
--- /dev/null
+++ b/src/osd/windows/wintime.c
@@ -0,0 +1,159 @@
+//============================================================
+//
+// wintime.c - Win32 OSD core timing functions
+//
+// Copyright (c) 1996-2007, Nicola Salmoria and the MAME Team.
+// Visit http://mamedev.org for licensing and usage restrictions.
+//
+//============================================================
+
+// standard windows headers
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#include <mmsystem.h>
+
+// MAME headers
+#include "osdepend.h"
+
+
+
+//============================================================
+// GLOBAL VARIABLES
+//============================================================
+
+static osd_ticks_t ticks_per_second = 0;
+static osd_ticks_t suspend_ticks = 0;
+static BOOL using_qpc = TRUE;
+
+
+
+//============================================================
+// osd_ticks
+//============================================================
+
+osd_ticks_t osd_ticks(void)
+{
+ LARGE_INTEGER performance_count;
+
+ // if we're suspended, just return that
+ if (suspend_ticks != 0)
+ return suspend_ticks;
+
+ // if we have a per second count, just go for it
+ if (ticks_per_second != 0)
+ {
+ // QueryPerformanceCounter if we can
+ if (using_qpc)
+ {
+ QueryPerformanceCounter(&performance_count);
+ return (osd_ticks_t)performance_count.QuadPart - suspend_ticks;
+ }
+
+ // otherwise, fall back to timeGetTime
+ else
+ return (osd_ticks_t)timeGetTime() - suspend_ticks;
+ }
+
+ // if not, we have to determine it
+ using_qpc = QueryPerformanceFrequency(&performance_count) && (performance_count.QuadPart != 0);
+ if (using_qpc)
+ ticks_per_second = (osd_ticks_t)performance_count.QuadPart;
+ else
+ ticks_per_second = 1000;
+
+ // call ourselves to get the first value
+ return osd_ticks();
+}
+
+
+//============================================================
+// osd_ticks_per_second
+//============================================================
+
+osd_ticks_t osd_ticks_per_second(void)
+{
+ return ticks_per_second;
+}
+
+
+//============================================================
+// osd_profiling_ticks
+//============================================================
+
+#ifdef _MSC_VER
+
+#ifdef PTR64
+
+osd_ticks_t osd_profiling_ticks(void)
+{
+ return __rdtsc();
+}
+
+#else
+
+osd_ticks_t osd_profiling_ticks(void)
+{
+ INT64 result;
+ INT64 *presult = &result;
+
+ __asm {
+ __asm _emit 0Fh __asm _emit 031h // rdtsc
+ mov ebx, presult
+ mov [ebx],eax
+ mov [ebx+4],edx
+ }
+
+ return result;
+}
+
+#endif
+
+#else
+
+osd_ticks_t osd_profiling_ticks(void)
+{
+ INT64 result;
+
+ // use RDTSC
+ __asm__ __volatile__ (
+ "rdtsc"
+ : "=A" (result)
+ );
+
+ return result;
+}
+
+#endif
+
+
+//============================================================
+// osd_sleep
+//============================================================
+
+void osd_sleep(osd_ticks_t duration)
+{
+ DWORD msec;
+
+ // make sure we've computed ticks_per_second
+ if (ticks_per_second == 0)
+ (void)osd_ticks();
+
+ // convert to milliseconds, rounding down
+ msec = (DWORD)(duration * 1000 / ticks_per_second);
+
+ // only sleep if at least 2 full milliseconds
+ if (msec >= 2)
+ {
+ HANDLE current_thread = GetCurrentThread();
+ int old_priority = GetThreadPriority(current_thread);
+
+ // take a couple of msecs off the top for good measure
+ msec -= 2;
+
+ // bump our thread priority super high so that we get
+ // priority when we need it
+ SetThreadPriority(current_thread, THREAD_PRIORITY_TIME_CRITICAL);
+ Sleep(msec);
+ SetThreadPriority(current_thread, old_priority);
+ }
+}
diff --git a/src/osd/windows/winutf8.c b/src/osd/windows/winutf8.c
new file mode 100644
index 00000000000..38f67555402
--- /dev/null
+++ b/src/osd/windows/winutf8.c
@@ -0,0 +1,260 @@
+//============================================================
+//
+// winutf8.c - Win32 OSD core utility functions
+//
+// Copyright (c) 1996-2007, Nicola Salmoria and the MAME Team.
+// Visit http://mamedev.org for licensing and usage restrictions.
+//
+//============================================================
+
+// standard windows headers
+#define _WIN32_WINNT 0x0400
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#include <shellapi.h>
+
+// MAMEOS headers
+#include "winutf8.h"
+#include "strconv.h"
+
+
+//============================================================
+// win_output_debug_string_utf8
+//============================================================
+
+void win_output_debug_string_utf8(const char *string)
+{
+ TCHAR *t_string = tstring_from_utf8(string);
+ if (t_string != NULL)
+ {
+ OutputDebugString(t_string);
+ free(t_string);
+ }
+}
+
+
+
+//============================================================
+// win_get_module_file_name_utf8
+//============================================================
+
+DWORD win_get_module_file_name_utf8(HMODULE module, char *filename, DWORD size)
+{
+ TCHAR t_filename[MAX_PATH];
+ char *utf8_filename;
+
+ if (GetModuleFileName(module, t_filename, ARRAY_LENGTH(t_filename)) == 0)
+ return 0;
+
+ utf8_filename = utf8_from_tstring(t_filename);
+ if (!utf8_filename)
+ return 0;
+
+ size = (DWORD) snprintf(filename, size, "%s", utf8_filename);
+ free(utf8_filename);
+ return size;
+}
+
+
+
+//============================================================
+// win_set_file_attributes_utf8
+//============================================================
+
+BOOL win_set_file_attributes_utf8(const char* filename, DWORD fileattributes)
+{
+ BOOL result = FALSE;
+ TCHAR* t_filename = tstring_from_utf8(filename);
+ if( !t_filename )
+ return result;
+
+ result = SetFileAttributes(t_filename, fileattributes);
+
+ free(t_filename);
+
+ return result;
+}
+
+
+
+//============================================================
+// win_copy_file_utf8
+//============================================================
+
+BOOL win_copy_file_utf8(const char* existingfilename, const char* newfilename, BOOL failifexists)
+{
+ TCHAR* t_existingfilename;
+ TCHAR* t_newfilename;
+ BOOL result = FALSE;
+
+ t_existingfilename = tstring_from_utf8(existingfilename);
+ if( !t_existingfilename )
+ return result;
+
+ t_newfilename = tstring_from_utf8(newfilename);
+ if( !t_newfilename ) {
+ free(t_existingfilename);
+ return result;
+ }
+
+ result = CopyFile(t_existingfilename, t_newfilename, failifexists);
+
+ free(t_newfilename);
+ free(t_existingfilename);
+
+ return result;
+}
+
+
+
+//============================================================
+// win_move_file_utf8
+//============================================================
+
+BOOL win_move_file_utf8(const char* existingfilename, const char* newfilename)
+{
+ TCHAR* t_existingfilename;
+ TCHAR* t_newfilename;
+ BOOL result = FALSE;
+
+ t_existingfilename = tstring_from_utf8(existingfilename);
+ if( !t_existingfilename )
+ return result;
+
+ t_newfilename = tstring_from_utf8(newfilename);
+ if( !t_newfilename ) {
+ free(t_existingfilename);
+ return result;
+ }
+
+ result = MoveFile(t_existingfilename, t_newfilename);
+
+ free(t_newfilename);
+ free(t_existingfilename);
+
+ return result;
+}
+
+
+
+//============================================================
+// win_message_box_utf8
+//============================================================
+
+int win_message_box_utf8(HWND window, const char *text, const char *caption, UINT type)
+{
+ int result = IDNO;
+ TCHAR *t_text = NULL;
+ TCHAR *t_caption = NULL;
+
+ if (text)
+ {
+ t_text = tstring_from_utf8(text);
+ if (!t_text)
+ goto done;
+ }
+
+ if (caption)
+ {
+ t_caption = tstring_from_utf8(caption);
+ if (!t_caption)
+ goto done;
+ }
+
+ result = MessageBox(window, t_text, t_caption, type);
+
+done:
+ if (t_text)
+ free(t_text);
+ if (t_caption)
+ free(t_caption);
+ return result;
+}
+
+
+
+//============================================================
+// win_set_window_text_utf8
+//============================================================
+
+BOOL win_set_window_text_utf8(HWND window, const char *text)
+{
+ BOOL result = FALSE;
+ TCHAR *t_text = NULL;
+
+ if (text)
+ {
+ t_text = tstring_from_utf8(text);
+ if (!t_text)
+ goto done;
+ }
+
+ result = SetWindowText(window, t_text);
+
+done:
+ if (t_text)
+ free(t_text);
+ return result;
+}
+
+
+
+//============================================================
+// win_get_window_text_utf8
+//============================================================
+
+int win_get_window_text_utf8(HWND window, char *buffer, size_t buffer_size)
+{
+ int result = 0;
+ char *utf8_buffer = NULL;
+ TCHAR t_buffer[256];
+
+ t_buffer[0] = '\0';
+
+ // invoke the core Win32 API
+ GetWindowText(window, t_buffer, ARRAY_LENGTH(t_buffer));
+
+ utf8_buffer = utf8_from_tstring(t_buffer);
+ if (!utf8_buffer)
+ goto done;
+
+ result = snprintf(buffer, buffer_size, "%s", utf8_buffer);
+
+done:
+ if (utf8_buffer)
+ free(utf8_buffer);
+ return result;
+}
+
+
+
+//============================================================
+// win_create_window_ex_utf8
+//============================================================
+
+HWND win_create_window_ex_utf8(DWORD exstyle, const char* classname, const char* windowname, DWORD style,
+ int x, int y, int width, int height, HWND parent, HMENU menu,
+ HINSTANCE instance, void* param)
+{
+ TCHAR* t_classname;
+ TCHAR* t_windowname;
+ HWND result = 0;
+
+ t_classname = tstring_from_utf8(classname);
+ if( !t_classname )
+ return result;
+
+ t_windowname = tstring_from_utf8(windowname);
+ if( !t_windowname ) {
+ free(t_classname);
+ return result;
+ }
+
+ result = CreateWindowEx(exstyle, t_classname, t_windowname, style, x, y, width, height, parent,
+ menu, instance, param);
+
+ free(t_windowname);
+ free(t_classname);
+
+ return result;
+}
diff --git a/src/osd/windows/winutf8.h b/src/osd/windows/winutf8.h
new file mode 100644
index 00000000000..137e111f957
--- /dev/null
+++ b/src/osd/windows/winutf8.h
@@ -0,0 +1,30 @@
+//============================================================
+//
+// winutf8.h - Win32 UTF-8 wrappers
+//
+// Copyright (c) 1996-2007, Nicola Salmoria and the MAME Team.
+// Visit http://mamedev.org for licensing and usage restrictions.
+//
+//============================================================
+
+#ifndef __WINUTF8__
+#define __WINUTF8__
+
+#include "osdcore.h"
+
+// wrappers for kernel32.dll
+void win_output_debug_string_utf8(const char *string);
+DWORD win_get_module_file_name_utf8(HMODULE module, char *filename, DWORD size);
+BOOL win_set_file_attributes_utf8(const char* filename, DWORD fileattributes);
+BOOL win_copy_file_utf8(const char* existingfilename, const char* newfilename, BOOL failifexists);
+BOOL win_move_file_utf8(const char* existingfilename, const char* newfilename);
+
+// wrappers for user32.dll
+int win_message_box_utf8(HWND window, const char *text, const char *caption, UINT type);
+BOOL win_set_window_text_utf8(HWND window, const char *text);
+int win_get_window_text_utf8(HWND window, char *buffer, size_t buffer_size);
+HWND win_create_window_ex_utf8(DWORD exstyle, const char* classname, const char* windowname, DWORD style,
+ int x, int y, int width, int height, HWND wndparent, HMENU menu,
+ HINSTANCE instance, void* param);
+
+#endif // __WINUTF8__
diff --git a/src/osd/windows/winutil.c b/src/osd/windows/winutil.c
new file mode 100644
index 00000000000..7197bd2303f
--- /dev/null
+++ b/src/osd/windows/winutil.c
@@ -0,0 +1,125 @@
+//============================================================
+//
+// winutil.c - Win32 OSD core utility functions
+//
+// Copyright (c) 1996-2007, Nicola Salmoria and the MAME Team.
+// Visit http://mamedev.org for licensing and usage restrictions.
+//
+//============================================================
+
+// standard windows headers
+#define _WIN32_WINNT 0x0400
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+
+// MAMEOS headers
+#include "winutil.h"
+#include "strconv.h"
+
+//============================================================
+// win_error_to_file_error
+//============================================================
+
+file_error win_error_to_file_error(DWORD error)
+{
+ file_error filerr;
+
+ // convert a Windows error to a file_error
+ switch (error)
+ {
+ case ERROR_SUCCESS:
+ filerr = FILERR_NONE;
+ break;
+
+ case ERROR_OUTOFMEMORY:
+ filerr = FILERR_OUT_OF_MEMORY;
+ break;
+
+ case ERROR_FILE_NOT_FOUND:
+ case ERROR_PATH_NOT_FOUND:
+ filerr = FILERR_NOT_FOUND;
+ break;
+
+ case ERROR_ACCESS_DENIED:
+ filerr = FILERR_ACCESS_DENIED;
+ break;
+
+ case ERROR_SHARING_VIOLATION:
+ filerr = FILERR_ALREADY_OPEN;
+ break;
+
+ default:
+ filerr = FILERR_FAILURE;
+ break;
+ }
+ return filerr;
+}
+
+
+
+//============================================================
+// win_attributes_to_entry_type
+//============================================================
+
+osd_dir_entry_type win_attributes_to_entry_type(DWORD attributes)
+{
+ if (attributes == 0xFFFFFFFF)
+ return ENTTYPE_NONE;
+ else if (attributes & FILE_ATTRIBUTE_DIRECTORY)
+ return ENTTYPE_DIR;
+ else
+ return ENTTYPE_FILE;
+}
+
+
+
+//============================================================
+// win_is_gui_application
+//============================================================
+
+BOOL win_is_gui_application(void)
+{
+ static BOOL is_gui_frontend;
+ static BOOL is_first_time = TRUE;
+ HMODULE module;
+ BYTE *image_ptr;
+ IMAGE_DOS_HEADER *dos_header;
+ IMAGE_NT_HEADERS *nt_headers;
+ IMAGE_OPTIONAL_HEADER *opt_header;
+
+ // is this the first time we've been ran?
+ if (is_first_time)
+ {
+ is_first_time = FALSE;
+
+ // get the current module
+ module = GetModuleHandle(NULL);
+ if (!module)
+ return FALSE;
+ image_ptr = (BYTE*) module;
+
+ // access the DOS header
+ dos_header = (IMAGE_DOS_HEADER *) image_ptr;
+ if (dos_header->e_magic != IMAGE_DOS_SIGNATURE)
+ return FALSE;
+
+ // access the NT headers
+ nt_headers = (IMAGE_NT_HEADERS *) ((BYTE*)(dos_header) + (DWORD)(dos_header->e_lfanew));
+ if (nt_headers->Signature != IMAGE_NT_SIGNATURE)
+ return FALSE;
+
+ // access the optional header
+ opt_header = &nt_headers->OptionalHeader;
+ switch (opt_header->Subsystem)
+ {
+ case IMAGE_SUBSYSTEM_WINDOWS_GUI:
+ is_gui_frontend = TRUE;
+ break;
+
+ case IMAGE_SUBSYSTEM_WINDOWS_CUI:
+ is_gui_frontend = FALSE;
+ break;
+ }
+ }
+ return is_gui_frontend;
+}
diff --git a/src/osd/windows/winutil.h b/src/osd/windows/winutil.h
new file mode 100644
index 00000000000..412686cb36b
--- /dev/null
+++ b/src/osd/windows/winutil.h
@@ -0,0 +1,20 @@
+//============================================================
+//
+// winutil.h - Win32 OSD core utility functions
+//
+// Copyright (c) 1996-2007, Nicola Salmoria and the MAME Team.
+// Visit http://mamedev.org for licensing and usage restrictions.
+//
+//============================================================
+
+#ifndef __WINUTIL__
+#define __WINUTIL__
+
+#include "osdcore.h"
+
+// Shared code
+file_error win_error_to_file_error(DWORD error);
+osd_dir_entry_type win_attributes_to_entry_type(DWORD attributes);
+BOOL win_is_gui_application(void);
+
+#endif // __WINUTIL__
diff --git a/src/osd/windows/winwork.c b/src/osd/windows/winwork.c
new file mode 100644
index 00000000000..602c25a15fc
--- /dev/null
+++ b/src/osd/windows/winwork.c
@@ -0,0 +1,775 @@
+//============================================================
+//
+// winwork.c - Win32 OSD core work item functions
+//
+// Copyright (c) 1996-2007, Nicola Salmoria and the MAME Team.
+// Visit http://mamedev.org for licensing and usage restrictions.
+//
+//============================================================
+
+// standard windows headers
+#define _WIN32_WINNT 0x0400
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#include <process.h>
+
+#ifdef __GNUC__
+#include <stdint.h>
+#endif
+
+// MAME headers
+#include "osdcore.h"
+
+
+//============================================================
+// DEBUGGING
+//============================================================
+
+#define KEEP_STATISTICS (0)
+
+
+
+//============================================================
+// PARAMETERS
+//============================================================
+
+#define SPIN_LOOP_TIME (osd_ticks_per_second() / 1000)
+
+
+
+//============================================================
+// MACROS
+//============================================================
+
+#if KEEP_STATISTICS
+#define add_to_stat(v,x) do { interlocked_add((v), (x)); } while (0)
+#define begin_timing(v) do { (v) -= osd_profiling_ticks(); } while (0)
+#define end_timing(v) do { (v) += osd_profiling_ticks(); } while (0)
+#else
+#define add_to_stat(v,x) do { } while (0)
+#define begin_timing(v) do { } while (0)
+#define end_timing(v) do { } while (0)
+#endif
+
+#ifndef YieldProcessor
+#ifdef __GNUC__
+INLINE void YieldProcessor(void)
+{
+ __asm__ __volatile__ ( "rep; nop" );
+}
+#else
+INLINE void YieldProcessor(void)
+{
+ __asm { rep nop }
+}
+#endif
+#endif
+
+
+
+//============================================================
+// TYPE DEFINITIONS
+//============================================================
+
+typedef struct _scalable_lock scalable_lock;
+struct _scalable_lock
+{
+ struct
+ {
+ volatile INT32 haslock; // do we have the lock?
+ INT32 filler[64/4-1]; // assumes a 64-byte cache line
+ } slot[WORK_MAX_THREADS]; // one slot per thread
+ volatile INT32 nextindex; // index of next slot to use
+};
+
+
+typedef struct _work_thread_info work_thread_info;
+struct _work_thread_info
+{
+ osd_work_queue * queue; // pointer back to the queue
+ HANDLE handle; // handle to the thread
+ HANDLE wakeevent; // wake event for the thread
+ volatile INT32 active; // are we actively processing work?
+
+#if KEEP_STATISTICS
+ INT32 itemsdone;
+ osd_ticks_t actruntime;
+ osd_ticks_t runtime;
+ osd_ticks_t spintime;
+ osd_ticks_t waittime;
+#endif
+};
+
+
+struct _osd_work_queue
+{
+ scalable_lock lock; // lock for protecting the queue
+ osd_work_item * volatile list; // list of items in the queue
+ osd_work_item ** volatile tailptr; // pointer to the tail pointer of work items in the queue
+ osd_work_item * volatile free; // free list of work items
+ volatile INT32 items; // items in the queue
+ volatile INT32 livethreads; // number of live threads
+ volatile INT32 waiting; // is someone waiting on the queue to complete?
+ volatile UINT8 exiting; // should the threads exit on their next opportunity?
+ UINT32 threads; // number of threads in this queue
+ UINT32 flags; // creation flags
+ work_thread_info * thread; // array of thread information
+ HANDLE doneevent; // event signalled when work is complete
+
+#if KEEP_STATISTICS
+ volatile INT32 itemsqueued; // total items queued
+ volatile INT32 setevents; // number of times we called SetEvent
+ volatile INT32 extraitems; // how many extra items we got after the first in the queue loop
+ volatile INT32 spinloops; // how many times spinning bought us more items
+#endif
+};
+
+
+struct _osd_work_item
+{
+ osd_work_item * next; // pointer to next item
+ osd_work_queue * queue; // pointer back to the owning queue
+ osd_work_callback callback; // callback function
+ void * param; // callback parameter
+ void * result; // callback result
+ HANDLE event; // event signalled when complete
+ UINT32 flags; // creation flags
+ volatile INT32 done; // is the item done?
+};
+
+
+
+//============================================================
+// FUNCTION PROTOTYPES
+//============================================================
+
+static int effective_num_processors(void);
+static unsigned __stdcall worker_thread_entry(void *param);
+static void worker_thread_process(osd_work_queue *queue, work_thread_info *thread);
+
+
+
+//============================================================
+// INLINE FUNCTIONS
+//============================================================
+
+INLINE void *compare_exchange_ptr(void * volatile *ptr, void *compare, void *exchange)
+{
+#ifdef PTR64
+ INT64 result = InterlockedCompareExchange64((LONGLONG *)ptr, (LONGLONG)exchange, (LONGLONG)compare);
+ return (void *)result;
+#else
+ INT32 result = InterlockedCompareExchange((LPLONG)ptr, (LONG)exchange, (LONG)compare);
+ return (void *)result;
+#endif
+}
+
+
+INLINE INT32 interlocked_exchange32(INT32 volatile *ptr, INT32 value)
+{
+ return InterlockedExchange((LPLONG)ptr, value);
+}
+
+
+INLINE INT32 interlocked_increment(INT32 volatile *ptr)
+{
+ return InterlockedIncrement((LPLONG)ptr);
+}
+
+
+INLINE INT32 interlocked_decrement(INT32 volatile *ptr)
+{
+ return InterlockedDecrement((LPLONG)ptr);
+}
+
+
+INLINE INT32 interlocked_add(INT32 volatile *ptr, INT32 add)
+{
+ return InterlockedExchangeAdd((LPLONG)ptr, add) + add;
+}
+
+
+
+//============================================================
+// Scalable Locks
+//============================================================
+
+INLINE void scalable_lock_init(scalable_lock *lock)
+{
+ memset(lock, 0, sizeof(*lock));
+ lock->slot[0].haslock = TRUE;
+}
+
+
+INLINE INT32 scalable_lock_acquire(scalable_lock *lock)
+{
+ INT32 myslot = (interlocked_increment(&lock->nextindex) - 1) & (WORK_MAX_THREADS - 1);
+ INT32 backoff = 1;
+
+ while (!lock->slot[myslot].haslock)
+ {
+ INT32 backcount;
+ for (backcount = 0; backcount < backoff; backcount++)
+ YieldProcessor();
+ backoff <<= 1;
+ }
+ lock->slot[myslot].haslock = FALSE;
+ return myslot;
+}
+
+
+INLINE void scalable_lock_release(scalable_lock *lock, INT32 myslot)
+{
+ interlocked_exchange32(&lock->slot[(myslot + 1) & (WORK_MAX_THREADS - 1)].haslock, TRUE);
+}
+
+
+
+//============================================================
+// osd_work_queue_alloc
+//============================================================
+
+osd_work_queue *osd_work_queue_alloc(int flags)
+{
+ int numprocs = effective_num_processors();
+ osd_work_queue *queue;
+ int threadnum;
+
+ // allocate a new queue
+ queue = malloc(sizeof(*queue));
+ if (queue == NULL)
+ goto error;
+ memset(queue, 0, sizeof(*queue));
+
+ // initialize basic queue members
+ queue->tailptr = (osd_work_item **)&queue->list;
+ queue->flags = flags;
+
+ // allocate events for the queue
+ queue->doneevent = CreateEvent(NULL, TRUE, TRUE, NULL); // manual reset, signalled
+ if (queue->doneevent == NULL)
+ goto error;
+
+ // initialize the critical section
+ scalable_lock_init(&queue->lock);
+
+ // determine how many threads to create...
+ // on a single-CPU system, create 1 thread for I/O queues, and 0 threads for everything else
+ if (numprocs == 1)
+ queue->threads = (flags & WORK_QUEUE_FLAG_IO) ? 1 : 0;
+
+ // on an n-CPU system, create (n-1) threads for multi queues, and 1 thread for everything else
+ else
+ queue->threads = (flags & WORK_QUEUE_FLAG_MULTI) ? (numprocs - 1) : 1;
+
+ // clamp to the maximum
+ queue->threads = MIN(queue->threads, WORK_MAX_THREADS);
+
+ // allocate memory for thread array (+1 to count the calling thread)
+ queue->thread = malloc((queue->threads + 1) * sizeof(queue->thread[0]));
+ if (queue->thread == NULL)
+ goto error;
+ memset(queue->thread, 0, (queue->threads + 1) * sizeof(queue->thread[0]));
+
+ // iterate over threads
+ for (threadnum = 0; threadnum < queue->threads; threadnum++)
+ {
+ work_thread_info *thread = &queue->thread[threadnum];
+ uintptr_t handle;
+
+ // set a pointer back to the queue
+ thread->queue = queue;
+
+ // create the per-thread wake event
+ thread->wakeevent = CreateEvent(NULL, FALSE, FALSE, NULL); // auto-reset, not signalled
+ if (thread->wakeevent == NULL)
+ goto error;
+
+ // create the thread
+ handle = _beginthreadex(NULL, 0, worker_thread_entry, thread, 0, NULL);
+ thread->handle = (HANDLE)handle;
+ if (thread->handle == NULL)
+ goto error;
+
+ // set its priority: I/O threads get high priority because they are assumed to be
+ // blocked most of the time; other threads just match the creator's priority
+ if (flags & WORK_QUEUE_FLAG_IO)
+ SetThreadPriority(thread->handle, THREAD_PRIORITY_ABOVE_NORMAL);
+ else
+ SetThreadPriority(thread->handle, GetThreadPriority(GetCurrentThread()));
+ }
+
+ // start a timer going for "waittime" on the main thread
+ begin_timing(queue->thread[queue->threads].waittime);
+ return queue;
+
+error:
+ osd_work_queue_free(queue);
+ return NULL;
+}
+
+
+//============================================================
+// osd_work_queue_items
+//============================================================
+
+int osd_work_queue_items(osd_work_queue *queue)
+{
+ // return the number of items currently in the queue
+ return queue->items;
+}
+
+
+//============================================================
+// osd_work_queue_wait
+//============================================================
+
+int osd_work_queue_wait(osd_work_queue *queue, osd_ticks_t timeout)
+{
+ // if no threads, no waiting
+ if (queue->threads == 0)
+ return TRUE;
+
+ // if no items, we're done
+ if (queue->items == 0)
+ return TRUE;
+
+ // if this is a multi queue, help out rather than doing nothing
+ if (queue->flags & WORK_QUEUE_FLAG_MULTI)
+ {
+ work_thread_info *thread = &queue->thread[queue->threads];
+ osd_ticks_t stopspin = osd_ticks() + timeout;
+
+ end_timing(thread->waittime);
+
+ // process what we can as a worker thread
+ worker_thread_process(queue, thread);
+
+ // if we're a high frequency queue, spin until done
+ if (queue->flags & WORK_QUEUE_FLAG_HIGH_FREQ)
+ {
+ // spin until we're done
+ begin_timing(thread->spintime);
+ while (queue->items != 0 && osd_ticks() < stopspin)
+ YieldProcessor();
+ end_timing(thread->spintime);
+
+ begin_timing(thread->waittime);
+ return (queue->items == 0);
+ }
+ begin_timing(thread->waittime);
+ }
+
+ // reset our done event and double-check the items before waiting
+ ResetEvent(queue->doneevent);
+ interlocked_exchange32(&queue->waiting, TRUE);
+ if (queue->items != 0)
+ WaitForSingleObject(queue->doneevent, timeout * 1000 / osd_ticks_per_second());
+ interlocked_exchange32(&queue->waiting, FALSE);
+
+ // return TRUE if we actually hit 0
+ return (queue->items == 0);
+}
+
+
+//============================================================
+// osd_work_queue_free
+//============================================================
+
+void osd_work_queue_free(osd_work_queue *queue)
+{
+ // if we have threads, clean them up
+ if (queue->threads > 0 && queue->thread != NULL)
+ {
+ int threadnum;
+
+ // stop the timer for "waittime" on the main thread
+ end_timing(queue->thread[queue->threads].waittime);
+
+ // signal all the threads to exit
+ queue->exiting = TRUE;
+ for (threadnum = 0; threadnum < queue->threads; threadnum++)
+ {
+ work_thread_info *thread = &queue->thread[threadnum];
+ if (thread->wakeevent != NULL)
+ SetEvent(thread->wakeevent);
+ }
+
+ // wait for all the threads to go away
+ for (threadnum = 0; threadnum < queue->threads; threadnum++)
+ {
+ work_thread_info *thread = &queue->thread[threadnum];
+
+ // block on the thread going away, then close the handle
+ if (thread->handle != NULL)
+ {
+ WaitForSingleObject(thread->handle, INFINITE);
+ CloseHandle(thread->handle);
+ }
+
+ // clean up the wake event
+ if (thread->wakeevent != NULL)
+ CloseHandle(thread->wakeevent);
+ }
+
+#if KEEP_STATISTICS
+ // output per-thread statistics
+ for (threadnum = 0; threadnum <= queue->threads; threadnum++)
+ {
+ work_thread_info *thread = &queue->thread[threadnum];
+ osd_ticks_t total = thread->runtime + thread->waittime + thread->spintime;
+ printf("Thread %d: items=%9d run=%5.2f%% (%5.2f%%) spin=%5.2f%% wait/other=%5.2f%%\n",
+ threadnum, thread->itemsdone,
+ (double)thread->runtime * 100.0 / (double)total,
+ (double)thread->actruntime * 100.0 / (double)total,
+ (double)thread->spintime * 100.0 / (double)total,
+ (double)thread->waittime * 100.0 / (double)total);
+ }
+#endif
+
+ // free the list
+ free(queue->thread);
+ }
+
+ // free all the events
+ if (queue->doneevent != NULL)
+ CloseHandle(queue->doneevent);
+
+ // free all items in the free list
+ while (queue->free != NULL)
+ {
+ osd_work_item *item = (osd_work_item *)queue->free;
+ queue->free = item->next;
+ if (item->event != NULL)
+ CloseHandle(item->event);
+ free(item);
+ }
+
+ // free all items in the active list
+ while (queue->list != NULL)
+ {
+ osd_work_item *item = (osd_work_item *)queue->list;
+ queue->list = item->next;
+ if (item->event != NULL)
+ CloseHandle(item->event);
+ free(item);
+ }
+
+#if KEEP_STATISTICS
+ printf("Items queued = %9d\n", queue->itemsqueued);
+ printf("SetEvent calls = %9d\n", queue->setevents);
+ printf("Extra items = %9d\n", queue->extraitems);
+ printf("Spin loops = %9d\n", queue->spinloops);
+#endif
+
+ // free the queue itself
+ free(queue);
+}
+
+
+//============================================================
+// osd_work_item_queue_multiple
+//============================================================
+
+osd_work_item *osd_work_item_queue_multiple(osd_work_queue *queue, osd_work_callback callback, INT32 numitems, void *parambase, INT32 paramstep, UINT32 flags)
+{
+ osd_work_item *itemlist = NULL;
+ osd_work_item **item_tailptr = &itemlist;
+ INT32 lockslot;
+ int itemnum;
+
+ // loop over items, building up a local list of work
+ for (itemnum = 0; itemnum < numitems; itemnum++)
+ {
+ osd_work_item *item;
+
+ // first allocate a new work item; try the free list first
+ do
+ {
+ item = (osd_work_item *)queue->free;
+ } while (item != NULL && compare_exchange_ptr((PVOID volatile *)&queue->free, item, item->next) != item);
+
+ // if nothing, allocate something new
+ if (item == NULL)
+ {
+ // allocate the item
+ item = malloc(sizeof(*item));
+ if (item == NULL)
+ return NULL;
+ item->event = NULL;
+ item->queue = queue;
+ }
+
+ // fill in the basics
+ item->next = NULL;
+ item->callback = callback;
+ item->param = parambase;
+ item->result = NULL;
+ item->flags = flags;
+ item->done = FALSE;
+
+ // advance to the next
+ *item_tailptr = item;
+ item_tailptr = &item->next;
+ parambase = (UINT8 *)parambase + paramstep;
+ }
+
+ // enqueue the whole thing within the critical section
+ lockslot = scalable_lock_acquire(&queue->lock);
+ *queue->tailptr = itemlist;
+ queue->tailptr = item_tailptr;
+ scalable_lock_release(&queue->lock, lockslot);
+
+ // increment the number of items in the queue
+ interlocked_add(&queue->items, numitems);
+ add_to_stat(&queue->itemsqueued, numitems);
+
+ // look for free threads to do the work
+ if (queue->livethreads < queue->threads)
+ {
+ int threadnum;
+
+ // iterate over all the threads
+ for (threadnum = 0; threadnum < queue->threads; threadnum++)
+ {
+ work_thread_info *thread = &queue->thread[threadnum];
+
+ // if this thread is not active, wake him up
+ if (!thread->active)
+ {
+ SetEvent(thread->wakeevent);
+ add_to_stat(&queue->setevents, 1);
+
+ // for non-shared, the first one we find is good enough
+ if (--numitems == 0)
+ break;
+ }
+ }
+ }
+
+ // if no threads, run the queue now on this thread
+ if (queue->threads == 0)
+ worker_thread_process(queue, &queue->thread[0]);
+
+ // only return the item if it won't get released automatically
+ return (flags & WORK_ITEM_FLAG_AUTO_RELEASE) ? NULL : *item_tailptr;
+}
+
+
+//============================================================
+// osd_work_item_wait
+//============================================================
+
+int osd_work_item_wait(osd_work_item *item, osd_ticks_t timeout)
+{
+ // if we're done already, just return
+ if (item->done)
+ return TRUE;
+
+ // if we don't have an event, create one
+ if (item->event == NULL)
+ item->event = CreateEvent(NULL, TRUE, FALSE, NULL); // manual reset, not signalled
+ else
+ ResetEvent(item->event);
+
+ // if we don't have an event, we need to spin (shouldn't ever really happen)
+ if (item->event != NULL)
+ {
+ osd_ticks_t stopspin = osd_ticks() + timeout;
+ while (!item->done && osd_ticks() < stopspin)
+ YieldProcessor();
+ }
+
+ // otherwise, block on the event until done
+ else if (!item->done)
+ WaitForSingleObject(item->event, timeout * 1000 / osd_ticks_per_second());
+
+ // return TRUE if the refcount actually hit 0
+ return item->done;
+}
+
+
+//============================================================
+// osd_work_item_result
+//============================================================
+
+void *osd_work_item_result(osd_work_item *item)
+{
+ return item->result;
+}
+
+
+//============================================================
+// osd_work_item_release
+//============================================================
+
+void osd_work_item_release(osd_work_item *item)
+{
+ osd_work_item *next;
+
+ // make sure we're done first
+ osd_work_item_wait(item, 100 * osd_ticks_per_second());
+
+ // add us to the free list on our queue
+ do
+ {
+ next = (osd_work_item *)item->queue->free;
+ item->next = next;
+ } while (compare_exchange_ptr((PVOID volatile *)&item->queue->free, next, item) != next);
+}
+
+
+//============================================================
+// effective_num_processors
+//============================================================
+
+static int effective_num_processors(void)
+{
+ char *procsoverride;
+ SYSTEM_INFO info;
+ int numprocs = 0;
+
+ // if the OSDPROCESSORS environment variable is set, use that value if valid
+ procsoverride = getenv("OSDPROCESSORS");
+ if (procsoverride != NULL && sscanf(procsoverride, "%d", &numprocs) == 1 && numprocs > 0)
+ return numprocs;
+
+ // otherwise, fetch the info from the system
+ GetSystemInfo(&info);
+
+ // max out at 4 for now since scaling above that seems to do poorly
+ return MIN(info.dwNumberOfProcessors, 4);
+}
+
+
+//============================================================
+// worker_thread_entry
+//============================================================
+
+static unsigned __stdcall worker_thread_entry(void *param)
+{
+ work_thread_info *thread = param;
+ osd_work_queue *queue = thread->queue;
+
+ // loop until we exit
+ for ( ;; )
+ {
+ // block waiting for work or exit
+ DWORD result = WAIT_OBJECT_0;
+
+ // bail on exit, and only wait if there are no pending items in queue
+ if (!queue->exiting && queue->list == NULL)
+ {
+ begin_timing(thread->waittime);
+ result = WaitForSingleObject(thread->wakeevent, INFINITE);
+ end_timing(thread->waittime);
+ }
+ if (queue->exiting)
+ break;
+
+ // indicate that we are live
+ interlocked_exchange32(&thread->active, TRUE);
+ interlocked_increment(&queue->livethreads);
+
+ // process work items
+ for ( ;; )
+ {
+ osd_ticks_t stopspin;
+
+ // process as much as we can
+ worker_thread_process(queue, thread);
+
+ // if we're a high frequency queue, spin for a while before giving up
+ if (queue->flags & WORK_QUEUE_FLAG_HIGH_FREQ)
+ {
+ // spin for a while looking for more work
+ begin_timing(thread->spintime);
+ stopspin = osd_ticks() + SPIN_LOOP_TIME;
+ while (queue->list == NULL && osd_ticks() < stopspin)
+ YieldProcessor();
+ end_timing(thread->spintime);
+ }
+
+ // if nothing more, release the processor
+ if (queue->list == NULL)
+ break;
+ add_to_stat(&queue->spinloops, 1);
+ }
+
+ // decrement the live thread count
+ interlocked_exchange32(&thread->active, FALSE);
+ interlocked_decrement(&queue->livethreads);
+ }
+ return 0;
+}
+
+
+//============================================================
+// worker_thread_process
+//============================================================
+
+static void worker_thread_process(osd_work_queue *queue, work_thread_info *thread)
+{
+ int threadid = thread - queue->thread;
+
+ begin_timing(thread->runtime);
+
+ // loop until everything is processed
+ while (queue->list != NULL)
+ {
+ osd_work_item *item;
+ INT32 lockslot;
+
+ // use a critical section to synchronize the removal of items
+ lockslot = scalable_lock_acquire(&queue->lock);
+ {
+ // pull the item from the queue
+ item = (osd_work_item *)queue->list;
+ if (item != NULL)
+ {
+ queue->list = item->next;
+ if (queue->list == NULL)
+ queue->tailptr = (osd_work_item **)&queue->list;
+ }
+ }
+ scalable_lock_release(&queue->lock, lockslot);
+
+ // process non-NULL items
+ if (item != NULL)
+ {
+ // call the callback and stash the result
+ begin_timing(thread->actruntime);
+ item->result = (*item->callback)(item->param, threadid);
+ end_timing(thread->actruntime);
+
+ // decrement the item count after we are done
+ interlocked_decrement(&queue->items);
+ interlocked_exchange32(&item->done, TRUE);
+ add_to_stat(&thread->itemsdone, 1);
+
+ // if it's an auto-release item, release it
+ if (item->flags & WORK_ITEM_FLAG_AUTO_RELEASE)
+ osd_work_item_release(item);
+
+ // set the result and signal the event
+ else if (item->event != NULL)
+ {
+ SetEvent(item->event);
+ add_to_stat(&item->queue->setevents, 1);
+ }
+
+ // if we removed an item and there's still work to do, bump the stats
+ if (queue->list != NULL)
+ add_to_stat(&queue->extraitems, 1);
+ }
+ }
+
+ // we don't need to set the doneevent for multi queues because they spin
+ if (queue->waiting)
+ {
+ SetEvent(queue->doneevent);
+ add_to_stat(&queue->setevents, 1);
+ }
+
+ end_timing(thread->runtime);
+}