the old-new win32 port
authornotaz <notasas@gmail.com>
Mon, 5 Oct 2009 16:12:45 +0000 (16:12 +0000)
committernotaz <notasas@gmail.com>
Mon, 5 Oct 2009 16:12:45 +0000 (16:12 +0000)
git-svn-id: file:///home/notaz/opt/svn/PicoDrive/platform@804 be3aeb3a-fb24-0410-a615-afba39da0efa

21 files changed:
common/config.c
common/input.c
common/input.h
common/lprintf.h
common/menu.c
common/menu.h
common/plat.h
common/posix.h
win32/Makefile [new file with mode: 0644]
win32/direct.cpp [new file with mode: 0644]
win32/direct.h [new file with mode: 0644]
win32/dsnd.cpp [new file with mode: 0644]
win32/dsnd.h [new file with mode: 0644]
win32/in_vk.c [new file with mode: 0644]
win32/in_vk.h [new file with mode: 0644]
win32/main.c [new file with mode: 0644]
win32/main.h [new file with mode: 0644]
win32/plat.c [new file with mode: 0644]
win32/port_config.h [new file with mode: 0644]
win32/readme.txt [new file with mode: 0644]
win32/version.h [new file with mode: 0644]

index b2f55cc..a6d4f1d 100644 (file)
@@ -21,7 +21,12 @@ static char *mystrip(char *str);
 #include "emu.h"
 #include <pico/pico.h>
 
+// always output DOS endlines
+#ifdef _WIN32
+#define NL "\n"
+#else
 #define NL "\r\n"
+#endif
 
 static int seek_sect(FILE *f, const char *section)
 {
index e3cf071..8950535 100644 (file)
@@ -6,6 +6,7 @@
 #include "plat.h"
 #include "../linux/in_evdev.h"
 #include "../gp2x/in_gp2x.h"
+#include "../win32/in_vk.h"
 
 typedef struct
 {
@@ -237,6 +238,7 @@ int in_update(int *result)
        for (i = 0; i < in_dev_count; i++) {
                in_dev_t *dev = &in_devices[i];
                if (dev->probed && dev->binds != NULL) {
+                       // FIXME: this is stupid, make it indirect
                        switch (dev->drv_id) {
 #ifdef IN_EVDEV
                        case IN_DRVID_EVDEV:
@@ -248,6 +250,9 @@ int in_update(int *result)
                                ret |= in_gp2x_update(dev->drv_data, dev->binds, result);
                                break;
 #endif
+                       case IN_DRVID_VK:
+                               ret |= in_vk_update(dev->drv_data, dev->binds, result);
+                               break;
                        }
                }
        }
@@ -808,6 +813,7 @@ void in_init(void)
 #ifdef IN_EVDEV
        in_evdev_init(&in_drivers[IN_DRVID_EVDEV]);
 #endif
+       in_vk_init(&in_drivers[IN_DRVID_VK]);
 }
 
 #if 0
index 92f5414..06b3de5 100644 (file)
@@ -50,6 +50,7 @@ enum {
        IN_DRVID_UNKNOWN = 0,
        IN_DRVID_GP2X,
        IN_DRVID_EVDEV,
+       IN_DRVID_VK,
        IN_DRVID_COUNT,
 };
 
index 34ec6b7..48b8d57 100644 (file)
@@ -1,2 +1,10 @@
+#ifdef __cplusplus
+extern "C" {
+#endif
+
 extern void lprintf(const char *fmt, ...);
 
+#ifdef __cplusplus
+}
+#endif
+
index fa0480b..887e0cd 100644 (file)
@@ -23,7 +23,7 @@
 #include <pico/patch.h>\r
 \r
 static char static_buff[64];\r
-static char menu_error_msg[64] = { 0, };\r
+char menu_error_msg[64] = { 0, };\r
 static int  menu_error_time = 0;\r
 \r
 #ifndef UIQ3\r
index d3e49b2..5f7db7b 100644 (file)
@@ -159,6 +159,9 @@ void menu_plat_setup(int is_wiz);
 void text_out16(int x, int y, const char *texto, ...);
 void me_update_msg(const char *msg);
 
+void menu_romload_prepare(const char *rom_name);
+void menu_romload_end(void);
+
 void menu_loop(void);
 int  menu_loop_tray(void);
 
index e1fc29b..5580dd8 100644 (file)
@@ -15,9 +15,6 @@ void pemu_sound_start(void);
 void pemu_sound_stop(void);
 void pemu_sound_wait(void);
 
-void menu_romload_prepare(const char *rom_name);
-void menu_romload_end(void);
-
 void plat_early_init(void);
 void plat_init(void);
 void plat_finish(void);
@@ -44,7 +41,7 @@ int  plat_is_dir(const char *path);
 int  plat_wait_event(int *fds_hnds, int count, int timeout_ms);
 void plat_sleep_ms(int ms);
 
-/* timers, to be used for time diff and must refet to the same clock */
+/* timers, to be used for time diff and must refer to the same clock */
 unsigned int plat_get_ticks_ms(void);
 unsigned int plat_get_ticks_us(void);
 void plat_wait_till_us(unsigned int us);
index 2c2d98d..33ca96c 100644 (file)
@@ -1,11 +1,19 @@
 /* define POSIX stuff: dirent, scandir, getcwd, mkdir */
-#if defined(__linux__)
+#if defined(__linux__) || defined(__MINGW32__)
 
 #include <dirent.h>
 #include <unistd.h>
 #include <sys/stat.h>
 #include <sys/types.h>
 
+#ifdef __MINGW32__
+#warning hacks!
+#define mkdir(pathname,mode) mkdir(pathname)
+#define d_type d_ino
+#define DT_REG 0
+#define DT_DIR 0
+#endif
+
 #else
 
 #error "must provide posix"
diff --git a/win32/Makefile b/win32/Makefile
new file mode 100644 (file)
index 0000000..5885235
--- /dev/null
@@ -0,0 +1,129 @@
+# settings
+CROSS=i586-mingw32msvc-
+
+#use_musashi = 1
+use_fame = 1
+#use_mz80 = 1
+
+-include Makefile.local
+
+CC = $(CROSS)gcc
+CXX = $(CROSS)g++
+LD = $(CROSS)ld
+STRIP = $(CROSS)strip
+
+DEFINES = _UNZIP_SUPPORT IN_VK
+CFLAGS += -O2 -Wall -falign-functions=2 -ffast-math
+CFLAGS += -I../.. -I. -I../../zlib/ -Idirectx/include/
+LDFLAGS += -L. -Ldirectx/lib/ -lgdi32 -lcomdlg32 -lddraw -ldsound -ldxguid
+
+# frontend
+OBJS += main.o plat.o direct.o dsnd.o in_vk.o
+
+# common
+OBJS += platform/common/emu.o platform/common/menu.o \
+       platform/common/config.o platform/common/fonts.o platform/common/readpng.o \
+       platform/common/input.o
+
+# Pico
+OBJS += pico/area.o pico/cart.o pico/memory.o pico/pico.o pico/sek.o \
+       pico/videoport.o pico/draw2.o pico/draw.o pico/z80if.o pico/patch.o \
+       pico/mode4.o pico/sms.o pico/misc.o pico/eeprom.o pico/debug.o
+# Pico - CD
+OBJS += pico/cd/pico.o pico/cd/memory.o pico/cd/sek.o pico/cd/LC89510.o \
+       pico/cd/cd_sys.o pico/cd/cd_file.o pico/cd/cue.o pico/cd/gfx_cd.o \
+       pico/cd/area.o pico/cd/misc.o pico/cd/pcm.o pico/cd/buffering.o
+# Pico - 32X
+OBJS += pico/32x/32x.o pico/32x/memory.o pico/32x/draw.o pico/32x/pwm.o
+# Pico - Pico
+OBJS += pico/pico/pico.o pico/pico/memory.o pico/pico/xpcm.o
+# Pico - sound
+OBJS += pico/sound/sound.o pico/sound/sn76496.o pico/sound/ym2612.o pico/sound/mix.o
+# Pico - carthw
+OBJS += pico/carthw/carthw.o pico/carthw/svp/svp.o pico/carthw/svp/memory.o \
+       pico/carthw/svp/ssp16.o pico/carthw/svp/compiler.o
+# zlib
+OBJS += zlib/gzio.o zlib/inffast.o zlib/inflate.o zlib/inftrees.o zlib/trees.o \
+       zlib/deflate.o zlib/crc32.o zlib/adler32.o zlib/zutil.o zlib/compress.o zlib/uncompr.o
+# unzip
+OBJS += unzip/unzip.o unzip/unzip_stream.o
+# CPU cores
+ifeq "$(use_musashi)" "1"
+DEFINES += EMU_M68K
+OBJS += cpu/musashi/m68kops.o cpu/musashi/m68kcpu.o
+#OBJS += cpu/musashi/m68kdasm.o
+endif
+ifeq "$(use_fame)" "1"
+DEFINES += EMU_F68K
+OBJS += cpu/fame/famec.o
+endif
+# z80
+ifeq "$(use_mz80)" "1"
+DEFINES += _USE_MZ80
+OBJS += cpu/mz80/mz80.o
+else
+DEFINES += _USE_CZ80
+OBJS += cpu/cz80/cz80.o
+endif
+# sh2
+OBJS += cpu/sh2mame/sh2pico.o
+# misc
+ifeq "$(use_fame)" "1"
+ifeq "$(use_musashi)" "1"
+OBJS += pico/debugCPU.o
+endif
+endif
+
+CFLAGS += $(addprefix -D,$(DEFINES))
+CXXFLAGS = $(CFLAGS)
+
+vpath %.c = ../..
+
+DIRS = platform platform/gp2x platform/common pico pico/cd pico/pico pico/sound pico/carthw/svp \
+       pico/32x zlib unzip cpu cpu/musashi cpu/fame cpu/mz80 cpu/cz80 cpu/sh2mame
+
+TARGET = PicoDrive.exe
+
+all: mkdirs $(TARGET)
+clean: tidy
+       @$(RM) $(TARGET)
+tidy:
+       $(RM) $(OBJS) $(TARGET).map
+       rm -rf $(DIRS)
+
+$(TARGET) : $(OBJS)
+       @echo ">>>" $@
+       $(CC) $(CFLAGS) $^ $(LDFLAGS) -lm -lpng -Wl,-Map=$(TARGET).map -o $@
+       $(STRIP) $@
+
+mkdirs:
+       @mkdir -p $(DIRS)
+
+include ../common/revision.mak
+
+pico/carthw/svp/compiler.o : ../../pico/carthw/svp/gen_arm.c
+pico/pico.o pico/cd/pico.o : ../../pico/pico_cmn.c ../../pico/pico_int.h
+pico/memory.o pico/cd/memory.o : ../../pico/pico_int.h ../../pico/memory.h
+
+../../cpu/musashi/m68kops.c :
+       @make -C ../../cpu/musashi
+
+cpu/mz80/mz80.o : ../../cpu/mz80/mz80.asm
+       @echo $@
+       @nasm -f elf $< -o $@
+
+../../cpu/mz80/mz80.asm :
+       @make -C ../../cpu/mz80/
+
+.c.o:
+       @echo ">>>" $<
+       $(CC) $(CFLAGS) -c $< -o $@
+.s.o:
+       @echo ">>>" $<
+       $(CC) $(CFLAGS) -c $< -o $@
+
+
+cpu/fame/famec.o : ../../cpu/fame/famec.c ../../cpu/fame/famec_opcodes.h
+       @echo ">>>" $<
+       $(CC) $(CFLAGS) -Wno-unused -c $< -o $@
+
diff --git a/win32/direct.cpp b/win32/direct.cpp
new file mode 100644 (file)
index 0000000..033f87c
--- /dev/null
@@ -0,0 +1,410 @@
+// ddraw\r
+#include <ddraw.h>\r
+#include "../common/lprintf.h"\r
+#include "direct.h"\r
+#include "main.h"\r
+\r
+#define EmuWidth 320\r
+#define EmuHeight 240\r
+\r
+#define RELEASE(x) if (x) x->Release();  x=NULL;\r
+#define LOGFAIL() lprintf("fail: %s %s:%i\n", __FUNCTION__, __FILE__, __LINE__)\r
+\r
+static LPDIRECTDRAW7        m_pDD = NULL;\r
+static LPDIRECTDRAWSURFACE7 m_pddsFrontBuffer = NULL;\r
+static LPDIRECTDRAWSURFACE7 m_pddsBackBuffer = NULL;\r
+\r
+// quick and dirty stuff..\r
+void DirectExit(void)\r
+{\r
+  RELEASE(m_pddsBackBuffer);\r
+  RELEASE(m_pddsFrontBuffer);\r
+  RELEASE(m_pDD);\r
+}\r
+\r
+int DirectInit(void)\r
+{\r
+  HRESULT ret;\r
+  LPDIRECTDRAWCLIPPER pcClipper = NULL;\r
+  DDSURFACEDESC2 ddsd;\r
+\r
+  ret = DirectDrawCreateEx(NULL, (VOID**)&m_pDD, IID_IDirectDraw7, NULL);\r
+  if (ret) { LOGFAIL(); return 1; }\r
+\r
+  // Set cooperative level\r
+  ret = m_pDD->SetCooperativeLevel( FrameWnd, DDSCL_NORMAL );\r
+  if (ret) { LOGFAIL(); goto fail; }\r
+\r
+  // Create the primary surface\r
+  ZeroMemory( &ddsd, sizeof( ddsd ) );\r
+  ddsd.dwSize         = sizeof( ddsd );\r
+  ddsd.dwFlags        = DDSD_CAPS;\r
+  ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE;\r
+\r
+  ret = m_pDD->CreateSurface( &ddsd, &m_pddsFrontBuffer, NULL );\r
+  if (ret) { LOGFAIL(); goto fail; }\r
+\r
+  // Create the backbuffer surface\r
+  ddsd.dwFlags        = DDSD_CAPS | DDSD_WIDTH | DDSD_HEIGHT;\r
+  ddsd.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN | DDSCAPS_SYSTEMMEMORY;\r
+  ddsd.dwWidth        = EmuWidth;\r
+  ddsd.dwHeight       = EmuHeight;\r
+\r
+  ret = m_pDD->CreateSurface( &ddsd, &m_pddsBackBuffer, NULL );\r
+  if (ret) { LOGFAIL(); goto fail; }\r
+\r
+  // clipper\r
+  ret = m_pDD->CreateClipper( 0, &pcClipper, NULL );\r
+  if (ret) { LOGFAIL(); goto fail; }\r
+\r
+  ret = pcClipper->SetHWnd( 0, FrameWnd );\r
+  if (ret) { LOGFAIL(); goto fail; }\r
+\r
+  ret = m_pddsFrontBuffer->SetClipper( pcClipper );\r
+  if (ret) { LOGFAIL(); goto fail; }\r
+\r
+  RELEASE(pcClipper);\r
+  return 0;\r
+\r
+fail:\r
+  RELEASE(pcClipper);\r
+  DirectExit();\r
+  return 1;\r
+}\r
+\r
+int DirectScreen(const void *emu_screen)\r
+{\r
+  const unsigned short *ps = (const unsigned short *)emu_screen;\r
+  DDSURFACEDESC2 sd;\r
+  int ret, x, y;\r
+\r
+  memset(&sd, 0, sizeof(sd));\r
+  sd.dwSize = sizeof(sd);\r
+  ret = m_pddsBackBuffer->Lock(NULL, &sd, DDLOCK_SURFACEMEMORYPTR|DDLOCK_WAIT|DDLOCK_WRITEONLY, NULL);\r
+  if (ret) { LOGFAIL(); return 1; }\r
+\r
+  //lprintf("w: %i h: %i pi: %i pf: %i\n", sd.dwWidth, sd.dwHeight, sd.lPitch, sd.ddpfPixelFormat.dwRGBBitCount);\r
+\r
+  if (sd.ddpfPixelFormat.dwRGBBitCount == 32)\r
+  {\r
+    int *dst = (int *)sd.lpSurface;\r
+    for (y = 0; y < EmuHeight; y++)\r
+    {\r
+      for (x = 0; x < EmuWidth; x++)\r
+      {\r
+        int s = *ps++;\r
+        dst[x] = ((s&0xf800)<<8) | ((s&0x07e0)<<5) | ((s&0x001f)<<3);\r
+      }\r
+      dst = (int *)((char *)dst + sd.lPitch);\r
+    }\r
+  }\r
+  else if (sd.ddpfPixelFormat.dwRGBBitCount == 24) /* wine uses this for me */\r
+  {\r
+    void *dst = sd.lpSurface;\r
+    for (y = 0; y < EmuHeight; y++)\r
+    {\r
+      unsigned char *dst1 = (unsigned char *) dst;\r
+      for (x = 0; x < EmuWidth; x++, dst1 += 3)\r
+      {\r
+        int s = *ps++;\r
+       dst1[2] = (s&0xf800)>>8; dst1[1] = (s&0x07e0)>>3; dst1[0] = s<<3; // BGR\r
+      }\r
+      dst = (void *)((char *)dst + sd.lPitch);\r
+    }\r
+  }\r
+  else if (sd.ddpfPixelFormat.dwRGBBitCount == 16)\r
+  {\r
+    unsigned short *dst = (unsigned short *)sd.lpSurface;\r
+    for (y = 0; y < EmuHeight; y++)\r
+    {\r
+      memcpy(dst, ps, EmuWidth*2);\r
+      ps += EmuWidth;\r
+      dst = (unsigned short *)((char *)dst + sd.lPitch);\r
+    }\r
+  }\r
+  else\r
+  {\r
+    LOGFAIL();\r
+  }\r
+\r
+  ret = m_pddsBackBuffer->Unlock(NULL);\r
+  if (ret) { LOGFAIL(); return 1; }\r
+  return 0;\r
+}\r
+\r
+int DirectClear(unsigned int colour)\r
+{\r
+  int ret = 0;\r
+  DDBLTFX ddbltfx;\r
+  ZeroMemory( &ddbltfx, sizeof(ddbltfx) );\r
+  ddbltfx.dwSize      = sizeof(ddbltfx);\r
+  ddbltfx.dwFillColor = colour;\r
+\r
+  if (m_pddsBackBuffer != NULL)\r
+    ret = m_pddsBackBuffer->Blt( NULL, NULL, NULL, DDBLT_COLORFILL, &ddbltfx );\r
+  if (ret) { LOGFAIL(); return 1; }\r
+  return 0;\r
+}\r
+\r
+int DirectPresent(void)\r
+{\r
+  int ret = 0;\r
+  if (FrameRectMy.right - FrameRectMy.left > 0 && FrameRectMy.bottom - FrameRectMy.top > 0)\r
+    ret = m_pddsFrontBuffer->Blt(&FrameRectMy, m_pddsBackBuffer, &EmuScreenRect, DDBLT_WAIT, NULL);\r
+  if (ret) { LOGFAIL(); return 1; }\r
+  return 0;\r
+}\r
+\r
+\r
+/* D3D */\r
+#ifdef USE_D3D\r
+static IDirect3D8 *Direct3D=NULL;\r
+IDirect3DDevice8 *Device=NULL;\r
+IDirect3DSurface8 *DirectBack=NULL; // Back Buffer\r
+\r
+static IDirect3DVertexBuffer8 *VertexBuffer=NULL;\r
+\r
+struct CustomVertex\r
+{\r
+  float x,y,z; // Vertex cordinates\r
+  unsigned int colour;\r
+  float u,v; // Texture coordinates\r
+};\r
+#define D3DFVF_CUSTOMVERTEX (D3DFVF_XYZ|D3DFVF_DIFFUSE|D3DFVF_TEX1)\r
+\r
+static CustomVertex VertexList[4];\r
+\r
+int DirectInit()\r
+{\r
+  D3DPRESENT_PARAMETERS d3dpp;\r
+  D3DDISPLAYMODE mode;\r
+  int i,u,ret=0;\r
+\r
+  memset(&d3dpp,0,sizeof(d3dpp));\r
+  memset(&mode,0,sizeof(mode));\r
+\r
+  Direct3D=Direct3DCreate8(D3D_SDK_VERSION); if (Direct3D==NULL) return 1;\r
+\r
+  // Set up the structure used to create the D3D device:\r
+  d3dpp.BackBufferWidth =MainWidth;\r
+  d3dpp.BackBufferHeight=MainHeight;\r
+  d3dpp.BackBufferCount =1;\r
+  d3dpp.SwapEffect=D3DSWAPEFFECT_DISCARD;\r
+  d3dpp.MultiSampleType =D3DMULTISAMPLE_NONE;\r
+\r
+#ifdef _XBOX\r
+  d3dpp.BackBufferFormat=D3DFMT_X8R8G8B8;\r
+  d3dpp.FullScreen_RefreshRateInHz=60;\r
+#else\r
+  Direct3D->GetAdapterDisplayMode(D3DADAPTER_DEFAULT,&mode);\r
+  d3dpp.BackBufferFormat=mode.Format;\r
+  d3dpp.Windowed=1;\r
+  d3dpp.hDeviceWindow=FrameWnd;\r
+#endif\r
+\r
+  // Try to create a device with hardware vertex processing:\r
+  for (i=0;i<3;i++)\r
+  {\r
+    int behave=D3DCREATE_HARDWARE_VERTEXPROCESSING;\r
+\r
+    // Try software vertex processing:\r
+    if (i==1) behave=D3DCREATE_MIXED_VERTEXPROCESSING;\r
+    if (i==2) behave=D3DCREATE_SOFTWARE_VERTEXPROCESSING;\r
+\r
+    Direct3D->CreateDevice(D3DADAPTER_DEFAULT,D3DDEVTYPE_HAL,FrameWnd,\r
+        behave|D3DCREATE_MULTITHREADED,&d3dpp,&Device);\r
+    if (Device) break;\r
+  }\r
+\r
+  if (Device==NULL)\r
+  {\r
+#if 0\r
+    // try ref\r
+    Direct3D->CreateDevice(D3DADAPTER_DEFAULT,D3DDEVTYPE_REF,FrameWnd,\r
+        D3DCREATE_SOFTWARE_VERTEXPROCESSING|D3DCREATE_MULTITHREADED,&d3dpp,&Device);\r
+    if (Device==NULL) goto fail0;\r
+    HMODULE test = LoadLibrary("d3d8d.dll");\r
+    if (test != NULL) FreeLibrary(test);\r
+    else {\r
+      error("Sorry, but this program requires Direct3D with hardware acceleration.\n\n"\r
+            "You can try using Direct3D software emulation, but you have to install "\r
+            "DirectX SDK for it to work\n(it seems to be missing now).");\r
+      goto fail1;\r
+    }\r
+#else\r
+    goto fail1;\r
+#endif\r
+  }\r
+\r
+  Device->GetBackBuffer(0,D3DBACKBUFFER_TYPE_MONO,&DirectBack);\r
+  if (DirectBack==NULL) goto fail1;\r
+\r
+  Device->CreateVertexBuffer(sizeof(VertexList),0,D3DFVF_CUSTOMVERTEX,D3DPOOL_DEFAULT,&VertexBuffer);\r
+  if (VertexBuffer==NULL) goto fail2;\r
+\r
+  ret=TexScreenInit(); if (ret) goto fail3;\r
+\r
+  //FontInit();\r
+\r
+  Device->SetRenderState(D3DRS_LIGHTING,0); // Turn off lighting\r
+\r
+  // Set up texture modes:\r
+  Device->SetTextureStageState(0,D3DTSS_ADDRESSU,D3DTADDRESS_CLAMP);\r
+  Device->SetTextureStageState(0,D3DTSS_ADDRESSV,D3DTADDRESS_CLAMP);\r
+\r
+  return 0;\r
+\r
+fail3:\r
+  RELEASE(VertexBuffer)\r
+fail2:\r
+  RELEASE(DirectBack)\r
+fail1:\r
+  RELEASE(Device)\r
+fail0:\r
+  RELEASE(Direct3D)\r
+\r
+  return 1;\r
+}\r
+\r
+void DirectExit()\r
+{\r
+  TexScreenExit();\r
+\r
+  // d3d\r
+  RELEASE(VertexBuffer)\r
+  RELEASE(DirectBack)\r
+  RELEASE(Device)\r
+  RELEASE(Direct3D)\r
+}\r
+\r
+int DirectClear(unsigned int colour)\r
+{\r
+  if (Device != NULL) {\r
+    Device->Clear(0,NULL,D3DCLEAR_TARGET,colour,1.0f,0);\r
+    return 0;\r
+  }\r
+\r
+  return 1;\r
+}\r
+\r
+int DirectPresent()\r
+{\r
+  if (Device != NULL) {\r
+    Device->Present(NULL,NULL,NULL,NULL);\r
+    return 0;\r
+  }\r
+\r
+  return 1;\r
+}\r
+\r
+#define PI 3.14159265f\r
+\r
+static int MakeVertexList()\r
+{\r
+  struct CustomVertex *vert=NULL,*pv=NULL;\r
+  float dist=0.0f;\r
+  float scalex=0.0f,scaley=0.0f;\r
+  unsigned int colour=0xffffff;\r
+  float right=0.0f,bottom=0.0f;\r
+\r
+  if (LoopMode!=8) colour=0x102040;\r
+\r
+  dist=10.0f; scalex=dist*1.3333f; scaley=dist;\r
+\r
+  scalex*=640.0f/(float)MainWidth;\r
+  scaley*=448.0f/(float)MainHeight;\r
+\r
+  vert=VertexList;\r
+\r
+  // Put the vertices for the corners of the screen:\r
+  pv=vert;\r
+  pv->z=dist;\r
+  pv->x=-scalex; pv->y=scaley;\r
+  pv->colour=colour; pv++;\r
+\r
+  *pv=vert[0]; pv->x= scalex; pv->y= scaley; pv++;\r
+  *pv=vert[0]; pv->x=-scalex; pv->y=-scaley; pv++;\r
+  *pv=vert[0]; pv->x= scalex; pv->y=-scaley; pv++;\r
+\r
+  // Find where the screen images ends on the texture\r
+  right =(float)EmuWidth /(float)TexWidth;\r
+  bottom=(float)EmuHeight/(float)TexHeight;\r
+\r
+  // Write texture coordinates:\r
+  pv=vert;\r
+  pv->u=0.0f;  pv->v=0.00f;  pv++;\r
+  pv->u=right; pv->v=0.00f;  pv++;\r
+  pv->u=0.0f;  pv->v=bottom; pv++;\r
+  pv->u=right; pv->v=bottom; pv++;\r
+\r
+  return 0;\r
+}\r
+\r
+static int SetupMatrices()\r
+{\r
+  D3DXVECTOR3 eye ( 0.0f, 0.0f, 0.0f );\r
+  D3DXVECTOR3 look( 0.0f, 0.0f, 0.0f );\r
+  D3DXVECTOR3 up  ( 0.0f, 1.0f, 0.0f );\r
+  D3DXMATRIX mat;\r
+  float nudgex=0.0f,nudgey=0.0f;\r
+\r
+  memset(&mat,0,sizeof(mat));\r
+\r
+  mat.m[0][0]=mat.m[1][1]=mat.m[2][2]=mat.m[3][3]=1.0f;\r
+  Device->SetTransform(D3DTS_WORLD,&mat);\r
+\r
+  look.x=(float)Inp.axis[2]/2457.6f;\r
+  look.y=(float)Inp.axis[3]/2457.6f;\r
+  look.z=10.0f;\r
+\r
+  // Nudge pixels to the centre of each screen pixel:\r
+  nudgex=13.3333f/(float)(MainWidth <<1);\r
+  nudgey=10.0000f/(float)(MainHeight<<1);\r
+  eye.x +=nudgex; eye.y +=nudgey;\r
+  look.x+=nudgex; look.y+=nudgey;\r
+\r
+  D3DXMatrixLookAtLH(&mat,&eye,&look,&up);\r
+  Device->SetTransform(D3DTS_VIEW,&mat);\r
+\r
+  D3DXMatrixPerspectiveFovLH(&mat, 0.5f*PI, 1.3333f, 0.2f, 1000.0f);\r
+  Device->SetTransform(D3DTS_PROJECTION,&mat);\r
+  return 0;\r
+}\r
+\r
+int DirectScreen()\r
+{\r
+  unsigned char *lock=NULL;\r
+  int ret;\r
+\r
+  if (Device == NULL)\r
+    return DirectScreenDDraw();\r
+\r
+  // Copy the screen to the screen texture:\r
+#ifdef _XBOX\r
+  TexScreenSwizzle();\r
+#else\r
+  ret=TexScreenLinear();\r
+  if (ret) lprintf("TexScreenLinear failed\n");\r
+#endif\r
+\r
+  SetupMatrices();\r
+\r
+  MakeVertexList();\r
+\r
+  // Copy vertices in:\r
+  VertexBuffer->Lock(0,sizeof(VertexList),&lock,0);\r
+  if (lock==NULL) { lprintf("VertexBuffer->Lock failed\n"); return 1; }\r
+  memcpy(lock,VertexList,sizeof(VertexList));\r
+  VertexBuffer->Unlock();\r
+\r
+  Device->BeginScene();\r
+  Device->SetTexture(0,TexScreen);\r
+  Device->SetStreamSource(0,VertexBuffer,sizeof(CustomVertex));\r
+  Device->SetVertexShader(D3DFVF_CUSTOMVERTEX);\r
+  Device->DrawPrimitive(D3DPT_TRIANGLESTRIP,0,2);\r
+  Device->EndScene();\r
+\r
+  return 0;\r
+}\r
+#endif\r
+\r
diff --git a/win32/direct.h b/win32/direct.h
new file mode 100644 (file)
index 0000000..a064b3f
--- /dev/null
@@ -0,0 +1,15 @@
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+int  DirectInit(void);
+void DirectExit(void);
+
+int  DirectScreen(const void *emu_screen);
+int  DirectClear(unsigned int colour);
+int  DirectPresent(void);
+
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/win32/dsnd.cpp b/win32/dsnd.cpp
new file mode 100644 (file)
index 0000000..4e1930e
--- /dev/null
@@ -0,0 +1,165 @@
+//#pragma warning (disable:4201)\r
+#include <stdlib.h>\r
+#define WIN32_LEAN_AND_MEAN\r
+#include <windows.h>\r
+#include <mmsystem.h>\r
+#include <dsound.h>\r
+\r
+#include "dsnd.h"\r
+#include "../common/lprintf.h"\r
+\r
+#define NSEGS 4\r
+#define RELEASE(x) if (x) x->Release();  x=NULL;\r
+\r
+static LPDIRECTSOUND DSound;\r
+static LPDIRECTSOUNDBUFFER LoopBuffer;\r
+static LPDIRECTSOUNDNOTIFY DSoundNotify;\r
+static HANDLE seg_played_event;\r
+static int LoopLen, LoopWrite, LoopSeg; // bytes\r
+\r
+static int LoopBlank(void)\r
+{\r
+  void *mema=NULL,*memb=NULL;\r
+  DWORD sizea=0,sizeb=0;\r
+\r
+  LoopBuffer->Lock(0, LoopLen, &mema,&sizea, &memb,&sizeb, 0);\r
+  \r
+  if (mema) memset(mema,0,sizea);\r
+\r
+  LoopBuffer->Unlock(mema,sizea, memb,sizeb);\r
+\r
+  return 0;\r
+}\r
+\r
+int DSoundInit(HWND wnd_coop, int rate, int stereo, int seg_samples)\r
+{\r
+  DSBUFFERDESC dsbd;\r
+  WAVEFORMATEX wfx;\r
+  DSBPOSITIONNOTIFY notifies[NSEGS];\r
+  int i;\r
+\r
+  memset(&dsbd,0,sizeof(dsbd));\r
+  memset(&wfx,0,sizeof(wfx));\r
+\r
+  // Make wave format:\r
+  wfx.wFormatTag=WAVE_FORMAT_PCM;\r
+  wfx.nChannels=stereo ? 2 : 1;\r
+  wfx.nSamplesPerSec=rate;\r
+  wfx.wBitsPerSample=16;\r
+\r
+  wfx.nBlockAlign=(WORD)((wfx.nChannels*wfx.wBitsPerSample)>>3);\r
+  wfx.nAvgBytesPerSec=wfx.nBlockAlign*wfx.nSamplesPerSec;\r
+\r
+  // Create the DirectSound interface:\r
+  DirectSoundCreate(NULL,&DSound,NULL);\r
+  if (DSound==NULL) return 1;\r
+\r
+  LoopSeg = seg_samples * 2;\r
+  if (stereo)\r
+    LoopSeg *= 2;\r
+\r
+  LoopLen = LoopSeg * NSEGS;\r
+\r
+  DSound->SetCooperativeLevel(wnd_coop, DSSCL_PRIORITY);\r
+  dsbd.dwFlags=DSBCAPS_GLOBALFOCUS;  // Play in background\r
+  dsbd.dwFlags|=DSBCAPS_GETCURRENTPOSITION2|DSBCAPS_CTRLPOSITIONNOTIFY;\r
+\r
+  // Create the looping buffer:\r
+  dsbd.dwSize=sizeof(dsbd);\r
+  dsbd.dwBufferBytes=LoopLen;\r
+  dsbd.lpwfxFormat=&wfx;\r
+\r
+  DSound->CreateSoundBuffer(&dsbd,&LoopBuffer,NULL);\r
+  if (LoopBuffer==NULL) return 1;\r
+\r
+  LoopBuffer->QueryInterface(IID_IDirectSoundNotify, (LPVOID*)&DSoundNotify);\r
+  if (DSoundNotify == NULL) {\r
+    lprintf("QueryInterface(IID_IDirectSoundNotify) failed\n");\r
+    goto out;\r
+  }\r
+\r
+  seg_played_event = CreateEvent(NULL, 0, 0, NULL);\r
+  if (seg_played_event == NULL)\r
+    goto out;\r
+\r
+  for (i = 0; i < NSEGS; i++) {\r
+    notifies[i].dwOffset = i * LoopSeg;\r
+    notifies[i].hEventNotify = seg_played_event;\r
+  }\r
+  i = DSoundNotify->SetNotificationPositions(NSEGS, notifies);\r
+  if (i != DS_OK) {\r
+    lprintf("SetNotificationPositions failed\n");\r
+    goto out;\r
+  }\r
+\r
+out:\r
+  LoopBlank();\r
+  LoopBuffer->Play(0, 0, DSBPLAY_LOOPING);\r
+  return 0;\r
+}\r
+\r
+void DSoundExit(void)\r
+{\r
+  if (LoopBuffer)\r
+    LoopBuffer->Stop();\r
+  RELEASE(DSoundNotify);\r
+  RELEASE(LoopBuffer)\r
+  RELEASE(DSound)\r
+  CloseHandle(seg_played_event);\r
+  seg_played_event = NULL;\r
+}\r
+\r
+static int WriteSeg(const void *buff)\r
+{\r
+  void *mema=NULL,*memb=NULL;\r
+  DWORD sizea=0,sizeb=0;\r
+  int ret;\r
+\r
+  // Lock the segment at 'LoopWrite' and copy the next segment in\r
+  ret = LoopBuffer->Lock(LoopWrite, LoopSeg, &mema, &sizea, &memb, &sizeb, 0);\r
+  if (ret != DS_OK)\r
+    lprintf("LoopBuffer->Lock() failed: %i\n", ret);\r
+\r
+  if (mema) memcpy(mema,buff,sizea);\r
+//  if (memb) memcpy(memb,DSoundNext+sizea,sizeb);\r
+  if (sizeb != 0) lprintf("sizeb is not 0! (%i)\n", sizeb);\r
+\r
+  ret = LoopBuffer->Unlock(mema,sizea, memb, sizeb);\r
+  if (ret != DS_OK)\r
+    lprintf("LoopBuffer->Unlock() failed: %i\n", ret);\r
+\r
+  return 0;\r
+}\r
+\r
+int DSoundUpdate(const void *buff, int blocking)\r
+{\r
+  DWORD play = 0;\r
+  int pos;\r
+\r
+  LoopBuffer->GetCurrentPosition(&play, NULL);\r
+  pos = play;\r
+\r
+  // 'LoopWrite' is the next seg in the loop that we want to write\r
+  // First check that the sound 'play' pointer has moved out of it:\r
+  if (blocking) {\r
+    while (LoopWrite <= pos && pos < LoopWrite + LoopSeg) {\r
+      WaitForSingleObject(seg_played_event, 5000);\r
+      LoopBuffer->GetCurrentPosition(&play, NULL);\r
+      pos = play;\r
+    }\r
+  }\r
+  else {\r
+    if (LoopWrite <= pos && pos < LoopWrite + LoopSeg)\r
+      return 1;\r
+  }\r
+\r
+  WriteSeg(buff);\r
+\r
+  // Advance LoopWrite to next seg:\r
+  LoopWrite += LoopSeg;\r
+  if (LoopWrite + LoopSeg > LoopLen)\r
+    LoopWrite = 0;\r
+\r
+  return 0;\r
+}\r
+\r
diff --git a/win32/dsnd.h b/win32/dsnd.h
new file mode 100644 (file)
index 0000000..9132bcb
--- /dev/null
@@ -0,0 +1,14 @@
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+int  DSoundInit(HWND wnd_coop, int rate, int stereo, int seg_samples);
+void DSoundExit(void);
+int  DSoundUpdate(const void *buff, int blocking);
+
+extern short *DSoundNext;
+
+#ifdef __cplusplus
+}
+#endif
+
diff --git a/win32/in_vk.c b/win32/in_vk.c
new file mode 100644 (file)
index 0000000..625268a
--- /dev/null
@@ -0,0 +1,273 @@
+#define RC_INVOKED // we only need defines
+#include <winuser.h>
+#undef RC_INVOKED
+#include <string.h>
+
+#include "../common/input.h"
+#include "../common/emu.h" // array_size
+#include "in_vk.h"
+
+#define IN_VK_PREFIX "vk:"
+#define IN_VK_NKEYS 0x100
+
+static const char * const in_vk_prefix = IN_VK_PREFIX;
+static const char * const in_vk_keys[IN_VK_NKEYS] = {
+       [0 ... (IN_VK_NKEYS - 1)] = NULL,
+       [VK_LBUTTON] = "LBUTTON",       [VK_RBUTTON] = "RBUTTON",
+       [VK_TAB] = "TAB",               [VK_RETURN] = "RETURN",
+       [VK_SHIFT] = "SHIFT",           [VK_CONTROL] = "CONTROL",
+       [VK_LEFT] = "LEFT",             [VK_UP] = "UP",
+       [VK_RIGHT] = "RIGHT",           [VK_DOWN] = "DOWN",
+       [VK_SPACE] = "SPACE",
+};
+
+// additional player12 keys
+int in_vk_add_pl12;
+
+// up to 4, keyboards rarely allow more
+static int in_vk_keys_down[4];
+
+/*
+#define VK_END 35
+#define VK_HOME        36
+#define VK_INSERT      45
+#define VK_DELETE      46
+#define VK_NUMPAD0     0x60
+#define VK_NUMPAD1     0x61
+#define VK_NUMPAD2     0x62
+#define VK_NUMPAD3     0x63
+#define VK_NUMPAD4     0x64
+#define VK_NUMPAD5     0x65
+#define VK_NUMPAD6     0x66
+#define VK_NUMPAD7     0x67
+#define VK_NUMPAD8     0x68
+#define VK_NUMPAD9     0x69
+#define VK_MULTIPLY    0x6A
+#define VK_ADD 0x6B
+#define VK_SEPARATOR   0x6C
+#define VK_SUBTRACT    0x6D
+#define VK_DECIMAL     0x6E
+#define VK_DIVIDE      0x6F
+#define VK_F1  0x70
+#define VK_F2  0x71
+#define VK_F3  0x72
+#define VK_F4  0x73
+#define VK_F5  0x74
+#define VK_F6  0x75
+#define VK_F7  0x76
+#define VK_F8  0x77
+#define VK_F9  0x78
+#define VK_F10 0x79
+#define VK_F11 0x7A
+#define VK_F12 0x7B
+*/
+
+static void in_vk_probe(void)
+{
+       memset(in_vk_keys_down, 0, sizeof(in_vk_keys_down));
+       in_register(IN_VK_PREFIX "vk", IN_DRVID_VK, -1, (void *)1, IN_VK_NKEYS, 0);
+}
+
+static int in_vk_get_bind_count(void)
+{
+       return IN_VK_NKEYS;
+}
+
+/* ORs result with pressed buttons */
+int in_vk_update(void *drv_data, const int *binds, int *result)
+{
+       int i, t, k;
+
+       for (i = 0; i < array_size(in_vk_keys_down); i++) {
+               k = in_vk_keys_down[i];
+               if (!k)
+                       continue;
+
+               for (t = 0; t < IN_BINDTYPE_COUNT; t++)
+                       result[t] |= binds[IN_BIND_OFFS(k, t)];
+       }
+
+       result[IN_BINDTYPE_PLAYER12] |= in_vk_add_pl12;
+
+       return 0;
+}
+
+void in_vk_keydown(int kc)
+{
+       int i;
+
+       // search
+       for (i = 0; i < array_size(in_vk_keys_down); i++)
+               if (in_vk_keys_down[i] == kc)
+                       return;
+
+       // do
+       for (i = 0; i < array_size(in_vk_keys_down); i++) {
+               if (in_vk_keys_down[i] == 0) {
+                       in_vk_keys_down[i] = kc;
+                       return;
+               }
+       }
+}
+
+void in_vk_keyup(int kc)
+{
+       int i;
+       for (i = 0; i < array_size(in_vk_keys_down); i++)
+               if (in_vk_keys_down[i] == kc)
+                       in_vk_keys_down[i] = 0;
+}
+
+static int in_vk_update_keycode(void *data, int *is_down)
+{
+       return 0;
+}
+
+static const struct {
+       short key;
+       short pbtn;
+} key_pbtn_map[] =
+{
+       { VK_UP,        PBTN_UP },
+       { VK_DOWN,      PBTN_DOWN },
+       { VK_LEFT,      PBTN_LEFT },
+       { VK_RIGHT,     PBTN_RIGHT },
+       { VK_RETURN,    PBTN_MOK },
+/*
+       { BTN_X,        PBTN_MBACK },
+       { BTN_A,        PBTN_MA2 },
+       { BTN_Y,        PBTN_MA3 },
+       { BTN_L,        PBTN_L },
+       { BTN_R,        PBTN_R },
+       { BTN_SELECT,   PBTN_MENU },
+*/
+};
+
+#define KEY_PBTN_MAP_SIZE (sizeof(key_pbtn_map) / sizeof(key_pbtn_map[0]))
+
+static int in_vk_menu_translate(int keycode)
+{
+       int i;
+       if (keycode < 0)
+       {
+               /* menu -> kc */
+               keycode = -keycode;
+               for (i = 0; i < KEY_PBTN_MAP_SIZE; i++)
+                       if (key_pbtn_map[i].pbtn == keycode)
+                               return key_pbtn_map[i].key;
+       }
+       else
+       {
+               for (i = 0; i < KEY_PBTN_MAP_SIZE; i++)
+                       if (key_pbtn_map[i].key == keycode)
+                               return key_pbtn_map[i].pbtn;
+       }
+
+       return 0;
+}
+
+static int in_vk_get_key_code(const char *key_name)
+{
+       int i;
+
+       if (key_name[1] == 0 && 'A' <= key_name[0] && key_name[0] <= 'Z')
+               return key_name[0];
+
+       for (i = 0; i < IN_VK_NKEYS; i++) {
+               const char *k = in_vk_keys[i];
+               if (k != NULL && strcasecmp(k, key_name) == 0)
+                       return i;
+       }
+
+       return -1;
+}
+
+static const char *in_vk_get_key_name(int keycode)
+{
+       const char *name = NULL;
+       static char buff[4];
+
+       if ('A' <= keycode && keycode < 'Z') {
+               buff[0] = keycode;
+               buff[1] = 0;
+               return buff;
+       }
+
+       if (0 <= keycode && keycode < IN_VK_NKEYS)
+               name = in_vk_keys[keycode];
+       if (name == NULL)
+               name = "Unkn";
+       
+       return name;
+}
+
+static const struct {
+       short code;
+       char btype;
+       char bit;
+} in_vk_def_binds[] =
+{
+       /* MXYZ SACB RLDU */
+       { VK_UP,        IN_BINDTYPE_PLAYER12, 0 },
+       { VK_DOWN,      IN_BINDTYPE_PLAYER12, 1 },
+       { VK_LEFT,      IN_BINDTYPE_PLAYER12, 2 },
+       { VK_RIGHT,     IN_BINDTYPE_PLAYER12, 3 },
+       { 'S',          IN_BINDTYPE_PLAYER12, 4 },      /* B */
+       { 'D',          IN_BINDTYPE_PLAYER12, 5 },      /* C */
+       { 'A',          IN_BINDTYPE_PLAYER12, 6 },      /* A */
+       { VK_RETURN,    IN_BINDTYPE_PLAYER12, 7 },
+/*
+       { BTN_SELECT,   IN_BINDTYPE_EMU, PEVB_MENU },
+//     { BTN_Y,        IN_BINDTYPE_EMU, PEVB_SWITCH_RND },
+       { BTN_L,        IN_BINDTYPE_EMU, PEVB_STATE_SAVE },
+       { BTN_R,        IN_BINDTYPE_EMU, PEVB_STATE_LOAD },
+       { BTN_VOL_UP,   IN_BINDTYPE_EMU, PEVB_VOL_UP },
+       { BTN_VOL_DOWN, IN_BINDTYPE_EMU, PEVB_VOL_DOWN },
+*/
+};
+
+#define DEF_BIND_COUNT (sizeof(in_vk_def_binds) / sizeof(in_vk_def_binds[0]))
+
+static void in_vk_get_def_binds(int *binds)
+{
+       int i;
+
+       for (i = 0; i < DEF_BIND_COUNT; i++)
+               binds[IN_BIND_OFFS(in_vk_def_binds[i].code, in_vk_def_binds[i].btype)] =
+                       1 << in_vk_def_binds[i].bit;
+}
+
+/* remove binds of missing keys, count remaining ones */
+static int in_vk_clean_binds(void *drv_data, int *binds, int *def_binds)
+{
+       int i, count = 0;
+
+       for (i = 0; i < IN_VK_NKEYS; i++) {
+               int t, offs;
+               for (t = 0; t < IN_BINDTYPE_COUNT; t++) {
+                       offs = IN_BIND_OFFS(i, t);
+                       if (strcmp(in_vk_get_key_name(i), "Unkn") == 0)
+                               binds[offs] = def_binds[offs] = 0;
+                       if (binds[offs])
+                               count++;
+               }
+       }
+
+       return count;
+}
+
+void in_vk_init(void *vdrv)
+{
+       in_drv_t *drv = vdrv;
+
+       drv->prefix = in_vk_prefix;
+       drv->probe = in_vk_probe;
+       drv->get_bind_count = in_vk_get_bind_count;
+       drv->get_def_binds = in_vk_get_def_binds;
+       drv->clean_binds = in_vk_clean_binds;
+       drv->menu_translate = in_vk_menu_translate;
+       drv->get_key_code = in_vk_get_key_code;
+       drv->get_key_name = in_vk_get_key_name;
+       drv->update_keycode = in_vk_update_keycode;
+}
+
diff --git a/win32/in_vk.h b/win32/in_vk.h
new file mode 100644 (file)
index 0000000..8bc7e98
--- /dev/null
@@ -0,0 +1,16 @@
+#ifdef IN_VK
+
+void in_vk_init(void *vdrv);
+int  in_vk_update(void *drv_data, const int *binds, int *result);
+
+void in_vk_keydown(int kc);
+void in_vk_keyup(int kc);
+
+extern int in_vk_add_pl12;
+
+#else
+
+#define in_vk_init(x)
+#define in_vk_update(a,b,c) 0
+
+#endif
diff --git a/win32/main.c b/win32/main.c
new file mode 100644 (file)
index 0000000..17191e2
--- /dev/null
@@ -0,0 +1,635 @@
+#include <windows.h>\r
+#include <commdlg.h>\r
+#include <stdio.h>\r
+\r
+#include "../../pico/pico.h"\r
+#include "../common/readpng.h"\r
+#include "../common/config.h"\r
+#include "../common/lprintf.h"\r
+#include "../common/emu.h"\r
+#include "../common/menu.h"\r
+#include "../common/input.h"\r
+#include "../common/plat.h"\r
+#include "version.h"\r
+#include "direct.h"\r
+#include "in_vk.h"\r
+\r
+char *romname=NULL;\r
+HWND FrameWnd=NULL;\r
+RECT FrameRectMy;\r
+RECT EmuScreenRect = { 0, 0, 320, 224 };\r
+int lock_to_1_1 = 1;\r
+static HWND PicoSwWnd=NULL, PicoPadWnd=NULL;\r
+\r
+static HMENU mmain = 0, mdisplay = 0, mpicohw = 0;\r
+static HBITMAP ppad_bmp = 0;\r
+static HBITMAP ppage_bmps[7] = { 0, };\r
+static char rom_name[0x20*3+1];\r
+static int main_wnd_as_pad = 0;\r
+\r
+static HANDLE loop_enter_event, loop_end_event;\r
+\r
+void error(char *text)\r
+{\r
+  MessageBox(FrameWnd, text, "Error", 0);\r
+}\r
+\r
+static void UpdateRect(void)\r
+{\r
+  WINDOWINFO wi;\r
+  memset(&wi, 0, sizeof(wi));\r
+  wi.cbSize = sizeof(wi);\r
+  GetWindowInfo(FrameWnd, &wi);\r
+  FrameRectMy = wi.rcClient;\r
+}\r
+\r
+static int extract_rom_name(char *dest, const unsigned char *src, int len)\r
+{\r
+       char *p = dest, s_old = 0x20;\r
+       int i;\r
+\r
+       for (i = len - 1; i >= 0; i--)\r
+       {\r
+               if (src[i^1] != ' ') break;\r
+       }\r
+       len = i + 1;\r
+\r
+       for (i = 0; i < len; i++)\r
+       {\r
+               unsigned char s = src[i^1];\r
+               if (s == 0x20 && s_old == 0x20) continue;\r
+               else if (s >= 0x20 && s < 0x7f && s != '%')\r
+               {\r
+                       *p++ = s;\r
+               }\r
+               else\r
+               {\r
+                       sprintf(p, "%%%02x", s);\r
+                       p += 3;\r
+               }\r
+               s_old = s;\r
+       }\r
+       *p = 0;\r
+\r
+       return p - dest;\r
+}\r
+\r
+static void check_name_alias(const char *afname)\r
+{\r
+  char buff[256], *var, *val;\r
+  FILE *f;\r
+  int ret;\r
+\r
+  f = fopen(afname, "r");\r
+  if (f == NULL) return;\r
+\r
+  while (1)\r
+  {\r
+    ret = config_get_var_val(f, buff, sizeof(buff), &var, &val);\r
+    if (ret ==  0) break;\r
+    if (ret == -1) continue;\r
+\r
+    if (strcmp(rom_name, var) == 0) {\r
+      lprintf("rom aliased: \"%s\" -> \"%s\"\n", rom_name, val);\r
+      strncpy(rom_name, val, sizeof(rom_name));\r
+      break;\r
+    }\r
+  }\r
+  fclose(f);\r
+}\r
+\r
+static HBITMAP png2hb(const char *fname, int is_480)\r
+{\r
+  BITMAPINFOHEADER bih;\r
+  HBITMAP bmp;\r
+  void *bmem;\r
+  int ret;\r
+\r
+  bmem = calloc(1, is_480 ? 480*240*3 : 320*240*3);\r
+  if (bmem == NULL) return NULL;\r
+  ret = readpng(bmem, fname, is_480 ? READPNG_480_24 : READPNG_320_24);\r
+  if (ret != 0) {\r
+    free(bmem);\r
+    return NULL;\r
+  }\r
+\r
+  memset(&bih, 0, sizeof(bih));\r
+  bih.biSize = sizeof(bih);\r
+  bih.biWidth = is_480 ? 480 : 320;\r
+  bih.biHeight = -240;\r
+  bih.biPlanes = 1;\r
+  bih.biBitCount = 24;\r
+  bih.biCompression = BI_RGB;\r
+  bmp = CreateDIBitmap(GetDC(FrameWnd), &bih, CBM_INIT, bmem, (BITMAPINFO *)&bih, 0);\r
+  if (bmp == NULL)\r
+    lprintf("CreateDIBitmap failed with %i", GetLastError());\r
+\r
+  free(bmem);\r
+  return bmp;\r
+}\r
+\r
+static void PrepareForROM(void)\r
+{\r
+  unsigned char *rom_data = NULL;\r
+  int i, ret, show = PicoAHW & PAHW_PICO;\r
+  \r
+  PicoGetInternal(PI_ROM, (pint_ret_t *) &rom_data);\r
+  EnableMenuItem(mmain, 2, MF_BYPOSITION|(show ? MF_ENABLED : MF_GRAYED));\r
+  ShowWindow(PicoPadWnd, show ? SW_SHOWNA : SW_HIDE);\r
+  ShowWindow(PicoSwWnd, show ? SW_SHOWNA : SW_HIDE);\r
+  CheckMenuItem(mpicohw, 1210, show ? MF_CHECKED : MF_UNCHECKED);\r
+  CheckMenuItem(mpicohw, 1211, show ? MF_CHECKED : MF_UNCHECKED);\r
+  PostMessage(FrameWnd, WM_COMMAND, 1220 + PicoPicohw.page, 0);\r
+  DrawMenuBar(FrameWnd);\r
+  InvalidateRect(PicoSwWnd, NULL, 1);\r
+\r
+  PicoPicohw.pen_pos[0] =\r
+  PicoPicohw.pen_pos[1] = 0x8000;\r
+  in_vk_add_pl12 = 0;\r
+\r
+  ret = extract_rom_name(rom_name, rom_data + 0x150, 0x20);\r
+  if (ret == 0)\r
+    extract_rom_name(rom_name, rom_data + 0x130, 0x20);\r
+\r
+  if (show)\r
+  {\r
+    char path[MAX_PATH], *p;\r
+    GetModuleFileName(NULL, path, sizeof(path) - 32);\r
+    p = strrchr(path, '\\');\r
+    if (p == NULL) p = path;\r
+    else p++;\r
+    if (ppad_bmp == NULL) {\r
+      strcpy(p, "pico\\pad.png");\r
+      ppad_bmp = png2hb(path, 0);\r
+    }\r
+\r
+    strcpy(p, "pico\\alias.txt");\r
+    check_name_alias(path);\r
+\r
+    for (i = 0; i < 7; i++) {\r
+      if (ppage_bmps[i] != NULL) DeleteObject(ppage_bmps[i]);\r
+      sprintf(p, "pico\\%s_%i.png", rom_name, i);\r
+      ppage_bmps[i] = png2hb(path, 1);\r
+    }\r
+    // games usually don't have page 6, so just duplicate page 5.\r
+    if (ppage_bmps[6] == NULL && ppage_bmps[5] != NULL) {\r
+      sprintf(p, "pico\\%s_5.png", rom_name);\r
+      ppage_bmps[6] = png2hb(path, 1);\r
+    }\r
+  }\r
+}\r
+\r
+static void LoadROM(const char *cmdpath)\r
+{\r
+  char rompath[MAX_PATH];\r
+  int ret;\r
+\r
+  if (cmdpath != NULL && strlen(cmdpath)) {\r
+    strcpy(rompath, cmdpath + (cmdpath[0] == '\"' ? 1 : 0));\r
+    if (rompath[strlen(rompath)-1] == '\"')\r
+      rompath[strlen(rompath)-1] = 0;\r
+  }\r
+  else {\r
+    OPENFILENAME of; ZeroMemory(&of, sizeof(of));\r
+    rompath[sizeof(rompath) - 1] = 0;\r
+    strncpy(rompath, rom_fname_loaded, sizeof(rompath) - 1);\r
+    of.lStructSize = sizeof(of);\r
+    of.lpstrFilter = "ROMs, CD images\0*.smd;*.bin;*.gen;*.zip;*.32x;*.sms;*.iso;*.cso;*.cue\0"\r
+                     "whatever\0*.*\0";\r
+    of.lpstrFile = rompath;\r
+    of.nMaxFile = MAX_PATH;\r
+    of.Flags = OFN_FILEMUSTEXIST|OFN_HIDEREADONLY;\r
+    of.hwndOwner = FrameWnd;\r
+    if (!GetOpenFileName(&of))\r
+      return;\r
+  }\r
+\r
+  if (engineState == PGS_Running) {\r
+    engineState = PGS_Paused;\r
+    WaitForSingleObject(loop_end_event, 5000);\r
+  }\r
+\r
+  ret = emu_reload_rom(rompath);\r
+  if (ret == 0) {\r
+    extern char menu_error_msg[]; // HACK..\r
+    error(menu_error_msg);\r
+    return;\r
+  }\r
+\r
+  PrepareForROM();\r
+  engineState = PGS_Running;\r
+  SetEvent(loop_enter_event);\r
+}\r
+\r
+static const int rect_widths[4]  = { 320, 256, 640, 512 };\r
+static const int rect_heights[4] = { 224, 224, 448, 448 };\r
+\r
+// Window proc for the frame window:\r
+static LRESULT CALLBACK WndProc(HWND hwnd,UINT msg,WPARAM wparam,LPARAM lparam)\r
+{\r
+  POINT pt;\r
+  RECT rc;\r
+  int i;\r
+  switch (msg)\r
+  {\r
+    case WM_CLOSE:\r
+      PostQuitMessage(0);\r
+      return 0;\r
+    case WM_DESTROY:\r
+      FrameWnd = NULL; // Blank the handle\r
+      break;\r
+    case WM_SIZE:\r
+    case WM_MOVE:\r
+    case WM_SIZING:\r
+      UpdateRect();\r
+      if (lock_to_1_1 && FrameRectMy.right - FrameRectMy.left != 0 &&\r
+          (FrameRectMy.right - FrameRectMy.left != EmuScreenRect.right - EmuScreenRect.left ||\r
+           FrameRectMy.bottom - FrameRectMy.top != EmuScreenRect.bottom - EmuScreenRect.top)) {\r
+        lock_to_1_1 = 0;\r
+        CheckMenuItem(mdisplay, 1104, MF_UNCHECKED);\r
+      }\r
+      break;\r
+    case WM_COMMAND:\r
+      switch (LOWORD(wparam))\r
+      {\r
+        case 1000:\r
+          LoadROM(NULL);\r
+          break;\r
+        case 1001:\r
+          emu_reset_game();\r
+          return 0;\r
+        case 1002:\r
+          PostQuitMessage(0);\r
+          return 0;\r
+        case 1100:\r
+        case 1101:\r
+        case 1102:\r
+        case 1103:\r
+//          LoopWait=1; // another sync hack\r
+//          for (i = 0; !LoopWaiting && i < 10; i++) Sleep(10);\r
+          FrameRectMy.right  = FrameRectMy.left + rect_widths[wparam&3];\r
+          FrameRectMy.bottom = FrameRectMy.top  + rect_heights[wparam&3];\r
+          AdjustWindowRect(&FrameRectMy, WS_OVERLAPPEDWINDOW, 1);\r
+          MoveWindow(hwnd, FrameRectMy.left, FrameRectMy.top,\r
+            FrameRectMy.right-FrameRectMy.left, FrameRectMy.bottom-FrameRectMy.top, 1);\r
+          UpdateRect();\r
+          lock_to_1_1 = 0;\r
+          CheckMenuItem(mdisplay, 1104, MF_UNCHECKED);\r
+//          if (rom_loaded) LoopWait=0;\r
+          return 0;\r
+        case 1104:\r
+          lock_to_1_1 = !lock_to_1_1;\r
+          CheckMenuItem(mdisplay, 1104, lock_to_1_1 ? MF_CHECKED : MF_UNCHECKED);\r
+          /* FALLTHROUGH */\r
+        case 2000: // EmuScreenRect/FrameRectMy sync request\r
+          if (!lock_to_1_1)\r
+            return 0;\r
+          FrameRectMy.right  = FrameRectMy.left + (EmuScreenRect.right - EmuScreenRect.left);\r
+         FrameRectMy.bottom = FrameRectMy.top  + (EmuScreenRect.bottom - EmuScreenRect.top);\r
+          AdjustWindowRect(&FrameRectMy, WS_OVERLAPPEDWINDOW, 1);\r
+          MoveWindow(hwnd, FrameRectMy.left, FrameRectMy.top,\r
+            FrameRectMy.right-FrameRectMy.left, FrameRectMy.bottom-FrameRectMy.top, 1);\r
+          UpdateRect();\r
+          return 0;\r
+        case 1210:\r
+        case 1211:\r
+          i = IsWindowVisible((LOWORD(wparam)&1) ? PicoPadWnd : PicoSwWnd);\r
+          i = !i;\r
+          ShowWindow((LOWORD(wparam)&1) ? PicoPadWnd : PicoSwWnd, i ? SW_SHOWNA : SW_HIDE);\r
+          CheckMenuItem(mpicohw, LOWORD(wparam), i ? MF_CHECKED : MF_UNCHECKED);\r
+          return 0;\r
+        case 1212:\r
+          main_wnd_as_pad = !main_wnd_as_pad;\r
+          CheckMenuItem(mpicohw, 1212, main_wnd_as_pad ? MF_CHECKED : MF_UNCHECKED);\r
+          return 0;\r
+        case 1220:\r
+        case 1221:\r
+        case 1222:\r
+        case 1223:\r
+        case 1224:\r
+        case 1225:\r
+        case 1226:\r
+          PicoPicohw.page = LOWORD(wparam) % 10;\r
+          for (i = 0; i < 7; i++)\r
+            CheckMenuItem(mpicohw, 1220 + i, MF_UNCHECKED);\r
+          CheckMenuItem(mpicohw, 1220 + PicoPicohw.page, MF_CHECKED);\r
+          InvalidateRect(PicoSwWnd, NULL, 1);\r
+          return 0;\r
+        case 1300:\r
+          MessageBox(FrameWnd, plat_get_credits(), "About", 0);\r
+          return 0;\r
+      }\r
+      break;\r
+    case WM_TIMER:\r
+      GetCursorPos(&pt);\r
+      GetWindowRect(PicoSwWnd, &rc);\r
+      if (PtInRect(&rc, pt)) break;\r
+      GetWindowRect(PicoPadWnd, &rc);\r
+      if (PtInRect(&rc, pt)) break;\r
+      PicoPicohw.pen_pos[0] |= 0x8000;\r
+      PicoPicohw.pen_pos[1] |= 0x8000;\r
+      in_vk_add_pl12 = 0;\r
+      break;\r
+    case WM_LBUTTONDOWN: in_vk_add_pl12 |=  0x20; return 0;\r
+    case WM_LBUTTONUP:   in_vk_add_pl12 &= ~0x20; return 0;\r
+    case WM_MOUSEMOVE:\r
+      if (!main_wnd_as_pad) break;\r
+      PicoPicohw.pen_pos[0] = 0x03c + (320 * LOWORD(lparam) / (FrameRectMy.right - FrameRectMy.left));\r
+      PicoPicohw.pen_pos[1] = 0x1fc + (232 * HIWORD(lparam) / (FrameRectMy.bottom - FrameRectMy.top));\r
+      SetTimer(FrameWnd, 100, 1000, NULL);\r
+      break;\r
+    case WM_KEYDOWN:\r
+      if (wparam == VK_TAB) {\r
+        emu_reset_game();\r
+       break;\r
+      }\r
+      if (wparam == VK_ESCAPE) {\r
+        LoadROM(NULL);\r
+       break;\r
+      }\r
+      in_vk_keydown(wparam);\r
+      break;\r
+    case WM_KEYUP:\r
+      in_vk_keyup(wparam);\r
+      break;\r
+  }\r
+\r
+  return DefWindowProc(hwnd,msg,wparam,lparam);\r
+}\r
+\r
+static LRESULT CALLBACK PicoSwWndProc(HWND hwnd,UINT msg,WPARAM wparam,LPARAM lparam)\r
+{\r
+  PAINTSTRUCT ps;\r
+  HDC hdc, hdc2;\r
+\r
+  switch (msg)\r
+  {\r
+    case WM_DESTROY: PicoSwWnd=NULL; break;\r
+    case WM_LBUTTONDOWN: in_vk_add_pl12 |=  0x20; return 0;\r
+    case WM_LBUTTONUP:   in_vk_add_pl12 &= ~0x20; return 0;\r
+    case WM_MOUSEMOVE:\r
+      if (HIWORD(lparam) < 0x20) break;\r
+      PicoPicohw.pen_pos[0] = 0x03c + LOWORD(lparam) * 2/3;\r
+      PicoPicohw.pen_pos[1] = 0x2f8 + HIWORD(lparam) - 0x20;\r
+      SetTimer(FrameWnd, 100, 1000, NULL);\r
+      break;\r
+    case WM_KEYDOWN: in_vk_keydown(wparam); break;\r
+    case WM_KEYUP:   in_vk_keyup(wparam);   break;\r
+    case WM_PAINT:\r
+      hdc = BeginPaint(hwnd, &ps);\r
+      if (ppage_bmps[PicoPicohw.page] == NULL)\r
+      {\r
+        SelectObject(hdc, GetStockObject(DEFAULT_GUI_FONT));\r
+        SetTextColor(hdc, RGB(255, 255, 255));\r
+        SetBkColor(hdc, RGB(0, 0, 0));\r
+        TextOut(hdc, 2,  2, "missing PNGs for", 16);\r
+        TextOut(hdc, 2, 18, rom_name, strlen(rom_name));\r
+      }\r
+      else\r
+      {\r
+        hdc2 = CreateCompatibleDC(GetDC(FrameWnd));\r
+        SelectObject(hdc2, ppage_bmps[PicoPicohw.page]);\r
+        BitBlt(hdc, 0, 0, 480, 240, hdc2, 0, 0, SRCCOPY);\r
+        DeleteDC(hdc2);\r
+      }\r
+      EndPaint(hwnd, &ps);\r
+      return 0;\r
+    case WM_CLOSE:\r
+      ShowWindow(hwnd, SW_HIDE);\r
+      CheckMenuItem(mpicohw, 1210, MF_UNCHECKED);\r
+      return 0;\r
+  }\r
+\r
+  return DefWindowProc(hwnd,msg,wparam,lparam);\r
+}\r
+\r
+static LRESULT CALLBACK PicoPadWndProc(HWND hwnd,UINT msg,WPARAM wparam,LPARAM lparam)\r
+{\r
+  PAINTSTRUCT ps;\r
+  HDC hdc, hdc2;\r
+\r
+  switch (msg)\r
+  {\r
+    case WM_DESTROY: PicoPadWnd=NULL; break;\r
+    case WM_LBUTTONDOWN: in_vk_add_pl12 |=  0x20; return 0;\r
+    case WM_LBUTTONUP:   in_vk_add_pl12 &= ~0x20; return 0;\r
+    case WM_MOUSEMOVE:\r
+      PicoPicohw.pen_pos[0] = 0x03c + LOWORD(lparam);\r
+      PicoPicohw.pen_pos[1] = 0x1fc + HIWORD(lparam);\r
+      SetTimer(FrameWnd, 100, 1000, NULL);\r
+      break;\r
+    case WM_KEYDOWN: in_vk_keydown(wparam); break;\r
+    case WM_KEYUP:   in_vk_keyup(wparam);   break;\r
+    case WM_PAINT:\r
+      if (ppad_bmp == NULL) break;\r
+      hdc = BeginPaint(hwnd, &ps);\r
+      hdc2 = CreateCompatibleDC(GetDC(FrameWnd));\r
+      SelectObject(hdc2, ppad_bmp);\r
+      BitBlt(hdc, 0, 0, 320, 240, hdc2, 0, 0, SRCCOPY);\r
+      EndPaint(hwnd, &ps);\r
+      DeleteDC(hdc2);\r
+      return 0;\r
+    case WM_CLOSE:\r
+      ShowWindow(hwnd, SW_HIDE);\r
+      CheckMenuItem(mpicohw, 1211, MF_UNCHECKED);\r
+      return 0;\r
+  }\r
+\r
+  return DefWindowProc(hwnd,msg,wparam,lparam);\r
+}\r
+\r
+\r
+static int FrameInit()\r
+{\r
+  WNDCLASS wc;\r
+  RECT rect={0,0,0,0};\r
+  HMENU mfile;\r
+  int style=0;\r
+  int left=0,top=0,width=0,height=0;\r
+\r
+  memset(&wc,0,sizeof(wc));\r
+\r
+  // Register the window class:\r
+  wc.lpfnWndProc=WndProc;\r
+  wc.hInstance=GetModuleHandle(NULL);\r
+  wc.hCursor=LoadCursor(NULL,IDC_ARROW);\r
+  wc.hbrBackground=CreateSolidBrush(0);\r
+  wc.lpszClassName="PicoMainFrame";\r
+  RegisterClass(&wc);\r
+\r
+  wc.lpszClassName="PicoSwWnd";\r
+  wc.lpfnWndProc=PicoSwWndProc;\r
+  RegisterClass(&wc);\r
+\r
+  wc.lpszClassName="PicoPadWnd";\r
+  wc.lpfnWndProc=PicoPadWndProc;\r
+  RegisterClass(&wc);\r
+\r
+  rect.right =320;\r
+  rect.bottom=224;\r
+\r
+  // Adjust size of windows based on borders:\r
+  style=WS_OVERLAPPEDWINDOW;\r
+  AdjustWindowRect(&rect,style,1);\r
+  width =rect.right-rect.left;\r
+  height=rect.bottom-rect.top;\r
+\r
+  // Place window in the centre of the screen:\r
+  SystemParametersInfo(SPI_GETWORKAREA,0,&rect,0);\r
+  left=rect.left+rect.right;\r
+  top=rect.top+rect.bottom;\r
+\r
+  left-=width; left>>=1;\r
+  top-=height; top>>=1;\r
+\r
+  // Create menu:\r
+  mfile = CreateMenu();\r
+  InsertMenu(mfile, -1, MF_BYPOSITION|MF_STRING, 1000, "&Load ROM");\r
+  InsertMenu(mfile, -1, MF_BYPOSITION|MF_STRING, 1001, "&Reset");\r
+  InsertMenu(mfile, -1, MF_BYPOSITION|MF_STRING, 1002, "E&xit");\r
+  mdisplay = CreateMenu();\r
+  InsertMenu(mdisplay, -1, MF_BYPOSITION|MF_STRING, 1100, "320x224");\r
+  InsertMenu(mdisplay, -1, MF_BYPOSITION|MF_STRING, 1101, "256x224");\r
+  InsertMenu(mdisplay, -1, MF_BYPOSITION|MF_STRING, 1102, "640x448");\r
+  InsertMenu(mdisplay, -1, MF_BYPOSITION|MF_STRING, 1103, "512x448");\r
+  InsertMenu(mdisplay, -1, MF_BYPOSITION|MF_STRING, 1104, "Lock to 1:1");\r
+  mpicohw = CreateMenu();\r
+  InsertMenu(mpicohw, -1, MF_BYPOSITION|MF_STRING, 1210, "Show &Storyware");\r
+  InsertMenu(mpicohw, -1, MF_BYPOSITION|MF_STRING, 1211, "Show &Drawing pad");\r
+  InsertMenu(mpicohw, -1, MF_BYPOSITION|MF_STRING, 1212, "&Main window as pad");\r
+  InsertMenu(mpicohw, -1, MF_BYPOSITION|MF_SEPARATOR, 0, NULL);\r
+  InsertMenu(mpicohw, -1, MF_BYPOSITION|MF_STRING, 1220, "Title page (&0)");\r
+  InsertMenu(mpicohw, -1, MF_BYPOSITION|MF_STRING, 1221, "Page &1");\r
+  InsertMenu(mpicohw, -1, MF_BYPOSITION|MF_STRING, 1222, "Page &2");\r
+  InsertMenu(mpicohw, -1, MF_BYPOSITION|MF_STRING, 1223, "Page &3");\r
+  InsertMenu(mpicohw, -1, MF_BYPOSITION|MF_STRING, 1224, "Page &4");\r
+  InsertMenu(mpicohw, -1, MF_BYPOSITION|MF_STRING, 1225, "Page &5");\r
+  InsertMenu(mpicohw, -1, MF_BYPOSITION|MF_STRING, 1226, "Page &6");\r
+  mmain = CreateMenu();\r
+  InsertMenu(mmain, -1, MF_BYPOSITION|MF_STRING|MF_POPUP, (UINT_PTR) mfile,    "&File");\r
+  InsertMenu(mmain, -1, MF_BYPOSITION|MF_STRING|MF_POPUP, (UINT_PTR) mdisplay, "&Display");\r
+  InsertMenu(mmain, -1, MF_BYPOSITION|MF_STRING|MF_POPUP, (UINT_PTR) mpicohw,  "&Pico");\r
+  EnableMenuItem(mmain, 2, MF_BYPOSITION|MF_GRAYED);\r
+//  InsertMenu(mmain, -1, MF_BYPOSITION|MF_STRING|MF_POPUP, 1200, "&Config");\r
+  InsertMenu(mmain, -1, MF_BYPOSITION|MF_STRING, 1300, "&About");\r
+\r
+  // Create the window:\r
+  FrameWnd=CreateWindow("PicoMainFrame","PicoDrive " VERSION,style|WS_VISIBLE,\r
+    left,top,width,height,NULL,mmain,NULL,NULL);\r
+\r
+  CheckMenuItem(mdisplay, 1104, lock_to_1_1 ? MF_CHECKED : MF_UNCHECKED);\r
+  ShowWindow(FrameWnd, SW_NORMAL);\r
+  UpdateWindow(FrameWnd);\r
+  UpdateRect();\r
+\r
+  // create Pico windows\r
+  style = WS_OVERLAPPED|WS_CAPTION|WS_BORDER|WS_SYSMENU;\r
+  rect.left=rect.top=0;\r
+  rect.right =320;\r
+  rect.bottom=224;\r
+\r
+  AdjustWindowRect(&rect,style,1);\r
+  width =rect.right-rect.left;\r
+  height=rect.bottom-rect.top;\r
+\r
+  left += 326;\r
+  PicoSwWnd=CreateWindow("PicoSwWnd","Storyware",style,\r
+    left,top,width+160,height,FrameWnd,NULL,NULL,NULL);\r
+\r
+  top += 266;\r
+  PicoPadWnd=CreateWindow("PicoPadWnd","Drawing Pad",style,\r
+    left,top,width,height,FrameWnd,NULL,NULL,NULL);\r
+\r
+  return 0;\r
+}\r
+\r
+// --------------------\r
+\r
+static DWORD WINAPI work_thread(void *x)\r
+{\r
+  while (engineState != PGS_Quit) {\r
+    WaitForSingleObject(loop_enter_event, INFINITE);\r
+    if (engineState != PGS_Running)\r
+      continue;\r
+\r
+    printf("loop..\n");\r
+    emu_loop();\r
+    SetEvent(loop_end_event);\r
+  }\r
+\r
+  return 0;\r
+}\r
+\r
+// XXX: use main.c\r
+void xxinit(void)\r
+{\r
+  /* in_init() must go before config, config accesses in_ fwk */\r
+  in_init();\r
+  pemu_prep_defconfig();\r
+  emu_read_config(0, 0);\r
+  config_readlrom(PicoConfigFile);\r
+\r
+  plat_init();\r
+  in_probe();\r
+\r
+  emu_init();\r
+  menu_init();\r
+}\r
+\r
+\r
+int WINAPI WinMain(HINSTANCE p1, HINSTANCE p2, LPSTR cmdline, int p4)\r
+{\r
+  MSG msg;\r
+  DWORD tid = 0;\r
+  HANDLE thread;\r
+  int ret;\r
+\r
+  xxinit();\r
+  FrameInit();\r
+  ret = DirectInit();\r
+  if (ret)\r
+    goto end0;\r
+\r
+  loop_enter_event = CreateEvent(NULL, 0, 0, NULL);\r
+  if (loop_enter_event == NULL)\r
+    goto end0;\r
+\r
+  loop_end_event = CreateEvent(NULL, 0, 0, NULL);\r
+  if (loop_end_event == NULL)\r
+    goto end0;\r
+\r
+  thread = CreateThread(NULL, 0, work_thread, NULL, 0, &tid);\r
+  if (thread == NULL)\r
+    goto end0;\r
+\r
+  LoadROM(cmdline);\r
+\r
+  // Main window loop:\r
+  for (;;)\r
+  {\r
+    GetMessage(&msg,NULL,0,0);\r
+    if (msg.message==WM_QUIT) break;\r
+\r
+    TranslateMessage(&msg);\r
+    DispatchMessage(&msg);\r
+  }\r
+\r
+  // Signal thread to quit and wait for it to exit:\r
+  if (engineState == PGS_Running) {\r
+    engineState = PGS_Quit;\r
+    WaitForSingleObject(loop_end_event, 5000);\r
+  }\r
+  CloseHandle(thread); thread=NULL;\r
+\r
+  emu_write_config(0);\r
+  emu_finish();\r
+  //plat_finish();\r
+\r
+end0:\r
+  DirectExit();\r
+  DestroyWindow(FrameWnd);\r
+\r
+//  _CrtDumpMemoryLeaks();\r
+  return 0;\r
+}\r
+\r
diff --git a/win32/main.h b/win32/main.h
new file mode 100644 (file)
index 0000000..7186b52
--- /dev/null
@@ -0,0 +1,11 @@
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern HWND FrameWnd;
+extern RECT FrameRectMy;
+extern RECT EmuScreenRect;
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/win32/plat.c b/win32/plat.c
new file mode 100644 (file)
index 0000000..0c1dea6
--- /dev/null
@@ -0,0 +1,259 @@
+#include <windows.h>
+#include <stdio.h>
+
+#include "../common/lprintf.h"
+#include "../common/plat.h"
+#include "../common/emu.h"
+#include "../../pico/pico.h"
+#include "version.h"
+#include "direct.h"
+#include "dsnd.h"
+#include "main.h"
+
+static unsigned short screen_buff[320 * 240];
+static unsigned char PicoDraw2FB_[(8+320) * (8+240+8)];
+unsigned char *PicoDraw2FB = PicoDraw2FB_;
+
+char cpu_clk_name[] = "unused";
+
+void plat_init(void)
+{
+       g_screen_ptr = (void *)screen_buff;
+}
+
+int plat_is_dir(const char *path)
+{
+       return (GetFileAttributes(path) & FILE_ATTRIBUTE_DIRECTORY) ? 1 : 0;
+}
+
+unsigned int plat_get_ticks_ms(void)
+{
+       return GetTickCount();
+}
+
+unsigned int plat_get_ticks_us(void)
+{
+       // XXX: maybe performance counters?
+       return GetTickCount() * 1000;
+}
+
+void plat_wait_till_us(unsigned int us)
+{
+       int msdiff = (int)(us - plat_get_ticks_us()) / 1000;
+       if (msdiff > 6)
+;//            Sleep(msdiff - 6);
+       while (plat_get_ticks_us() < us)
+               ;
+}
+
+void plat_sleep_ms(int ms)
+{
+       Sleep(ms);
+}
+
+int plat_wait_event(int *fds_hnds, int count, int timeout_ms)
+{
+       return -1;
+}
+
+void pemu_prep_defconfig(void)
+{
+       memset(&defaultConfig, 0, sizeof(defaultConfig));
+       defaultConfig.EmuOpt    = 0x9d | EOPT_RAM_TIMINGS|EOPT_CONFIRM_SAVE|EOPT_EN_CD_LEDS;
+       defaultConfig.s_PicoOpt = POPT_EN_STEREO|POPT_EN_FM|POPT_EN_PSG|POPT_EN_Z80 |
+                                 POPT_EN_MCD_PCM|POPT_EN_MCD_CDDA|POPT_ACC_SPRITES |
+                                 POPT_EN_32X|POPT_EN_PWM;
+       defaultConfig.s_PicoOpt|= POPT_6BTN_PAD; // for xmen proto
+       defaultConfig.s_PsndRate = 44100;
+       defaultConfig.s_PicoRegion = 0; // auto
+       defaultConfig.s_PicoAutoRgnOrder = 0x184; // US, EU, JP
+       defaultConfig.s_PicoCDBuffers = 0;
+       defaultConfig.Frameskip = 0;
+}
+
+static int EmuScanBegin16(unsigned int num)
+{
+       DrawLineDest = (unsigned short *) g_screen_ptr + g_screen_width * num;
+
+       return 0;
+}
+
+void pemu_loop_prep(void)
+{
+       PicoDrawSetColorFormat(1);
+       PicoScanBegin = EmuScanBegin16;
+       pemu_sound_start();
+}
+
+void pemu_loop_end(void)
+{
+       pemu_sound_stop();
+}
+
+void pemu_forced_frame(int opts)
+{
+}
+
+void pemu_update_display(const char *fps, const char *notice_msg)
+{
+       DirectScreen(g_screen_ptr);
+       DirectPresent();
+}
+
+void plat_video_wait_vsync(void)
+{
+}
+
+void plat_video_toggle_renderer(int is_next, int force_16bpp, int is_menu)
+{
+       // this will auto-select SMS/32X renderers
+       PicoDrawSetColorFormat(1);
+}
+
+void emu_video_mode_change(int start_line, int line_count, int is_32cols)
+{
+       EmuScreenRect.left = is_32cols ? 32 : 0;
+       EmuScreenRect.right = is_32cols ? 256+32 : 320;
+       EmuScreenRect.top = start_line;
+       EmuScreenRect.bottom = start_line + line_count;
+
+       PostMessage(FrameWnd, WM_COMMAND, 0x20000 | 2000, 0);
+}
+
+static int sndbuff[2*44100/50/2 + 4];
+
+static void update_sound(int len)
+{
+       /* avoid writing audio when lagging behind to prevent audio lag */
+       if (PicoSkipFrame != 2)
+               DSoundUpdate(sndbuff, (currentConfig.EmuOpt & EOPT_NO_FRMLIMIT) ? 0 : 1);
+}
+
+void pemu_sound_start(void)
+{
+       int ret;
+
+       PsndOut = NULL;
+       currentConfig.EmuOpt &= ~EOPT_EXT_FRMLIMIT;
+
+       // prepare sound stuff
+       if (currentConfig.EmuOpt & EOPT_EN_SOUND)
+       {
+               PsndRerate(0);
+
+               ret = DSoundInit(FrameWnd, PsndRate, (PicoOpt & POPT_EN_STEREO) ? 1 : 0, PsndLen);
+               if (ret != 0) {
+                       lprintf("dsound init failed\n");
+                       return;
+               }
+
+               PsndOut = (void *)sndbuff;
+               PicoWriteSound = update_sound;
+               currentConfig.EmuOpt |= EOPT_EXT_FRMLIMIT;
+       }
+}
+
+void pemu_sound_stop(void)
+{
+       DSoundExit();
+}
+
+void pemu_sound_wait(void)
+{
+}
+
+int plat_get_root_dir(char *dst, int len)
+{
+       int ml;
+
+       ml = GetModuleFileName(NULL, dst, len);
+       while (ml > 0 && dst[ml] != '\\')
+               ml--;
+       if (ml != 0)
+               ml++;
+
+       dst[ml] = 0;
+       return ml;
+}
+
+void plat_status_msg_busy_first(const char *msg)
+{
+}
+
+void plat_status_msg_busy_next(const char *msg)
+{
+}
+
+void plat_status_msg_clear(void)
+{
+}
+
+void plat_video_menu_enter(int is_rom_loaded)
+{
+}
+
+void plat_video_menu_begin(void)
+{
+}
+
+void plat_video_menu_end(void)
+{
+}
+
+void plat_validate_config(void)
+{
+}
+
+void plat_update_volume(int has_changed, int is_up)
+{
+}
+
+const char *plat_get_credits(void)
+{
+       return "PicoDrive v" VERSION " minibeta (c) notaz, 2006-2009\n\n"
+               "Credits:\n"
+               "fDave: base code of PicoDrive\n"
+               "Chui: Fame/C\n"
+               "NJ: CZ80\n"
+               "MAME devs: YM2612, SN76496 and SH2 cores\n"
+               "Stéphane Dallongeville: base of Fame/C (C68K), CZ80\n\n"
+               "Special thanks (ideas, valuable information and stuff):\n"
+               "Charles MacDonald, Eke, Exophase, Haze, Lordus, Nemesis,\n"
+               "Pierpaolo Prazzoli, Rokas, Steve Snake, Tasco Deluxe.\n";
+}
+
+void plat_debug_cat(char *str)
+{
+}
+
+// required by pico
+int mp3_get_bitrate(FILE *f, int size)
+{
+       return 128;
+}
+
+void mp3_start_play(FILE *f, int pos)
+{
+}
+
+void mp3_update(int *buffer, int length, int stereo)
+{
+}
+
+// other
+void lprintf(const char *fmt, ...)
+{
+  char buf[512];
+  va_list val;
+
+  va_start(val, fmt);
+  vsnprintf(buf, sizeof(buf), fmt, val);
+  va_end(val);
+  OutputDebugString(buf);
+  printf("%s", buf);
+}
+
+// fake
+int alphasort() { return 0; }
+int scandir() { return 0; }
+
diff --git a/win32/port_config.h b/win32/port_config.h
new file mode 100644 (file)
index 0000000..04b4c93
--- /dev/null
@@ -0,0 +1,40 @@
+// port specific settings
+
+#ifndef PORT_CONFIG_H
+#define PORT_CONFIG_H
+
+#define NO_SYNC
+
+#define CASE_SENSITIVE_FS 0 // CS filesystem
+#define DONT_OPEN_MANY_FILES 0
+#define REDUCE_IO_CALLS 0
+
+#define SCREEN_SIZE_FIXED 0
+#define SCREEN_WIDTH  320
+#define SCREEN_HEIGHT 240
+
+// draw.c
+#define OVERRIDE_HIGHCOL 0
+
+// draw2.c
+#define START_ROW  0 // which row of tiles to start rendering at?
+#define END_ROW   28 // ..end
+
+// pico.c
+#define CAN_HANDLE_240_LINES   1
+
+#define SIMPLE_WRITE_SOUND     1
+#define mix_32_to_16l_stereo_lvl mix_32_to_16l_stereo
+
+#define EL_LOGMASK (EL_STATUS)
+
+//#define dprintf(f,...) printf("%05i:%03i: " f "\n",Pico.m.frame_count,Pico.m.scanline,##__VA_ARGS__)
+#define dprintf(x...)
+
+// platform
+#define PATH_SEP      "\\"
+#define PATH_SEP_C    '\\'
+#define MENU_X2       0
+
+#endif //PORT_CONFIG_H
+
diff --git a/win32/readme.txt b/win32/readme.txt
new file mode 100644 (file)
index 0000000..ba9ebff
--- /dev/null
@@ -0,0 +1,69 @@
+\r
+About\r
+-----\r
+\r
+This is a quick windows port of PicoDrive, a Megadrive / Genesis emulator for\r
+handheld devices. It was originally coded having ARM CPU based devices in mind\r
+(most work was done on GP2X version), but there is also a PSP port.\r
+\r
+The reason I'm sometimes doing windows versions is to show certain emulation\r
+possibilities, first release was to demonstrate SVP emulation (Virtua Racing),\r
+later Pico toy and X-Men 32X prototype. It is not to compete with other\r
+emulators like Kega Fusion and the likes.\r
+\r
+For more info, visit http://notaz.gp2x.de/svp.php\r
+\r
+\r
+Releases\r
+--------\r
+\r
+1.70  - preliminary 32X emulation, runs X-Men proto.\r
+1.45a - Few bugfixes and additions.\r
+1.45  - Added preliminary Sega Pico emulation.\r
+1.40b - Perspective fix thanks to Pierpaolo Prazzoli's info.\r
+1.40a - Tasco Deluxe's dithering fix.\r
+1.40  - first release.\r
+\r
+\r
+Controls\r
+--------\r
+\r
+These are currently hardcoded, keyboard only:\r
+\r
+PC      Gen/MD      Sega Pico\r
+-------+-----------+---------\r
+Enter:  Start\r
+A:      A\r
+S:      B           red button\r
+D:      C           pen push\r
+TAB:            (reset)\r
+Esc:           (load ROM)\r
+Arrows:          D-pad\r
+\r
+It is possible to change some things in config.cfg (it is created on exit),\r
+but possibilities are limited.\r
+\r
+\r
+Credits\r
+-------\r
+\r
+Vast majority of code written by notaz (notasasatgmailcom).\r
+\r
+A lot of work on making SVP emulation happen was done by Tasco Deluxe, my\r
+stuff is a continuation of his. Pierpaolo Prazzoli's information and his\r
+SSP1610 disassembler in MAME code helped a lot too.\r
+\r
+The original PicoDrive was written by fDave from finalburn.com\r
+\r
+This PicoDrive version uses bits and pieces of from other projects:\r
+\r
+68k: FAME/C core, by Chui and Stéphane Dallongeville (as C68K).\r
+z80: CZ80 by Stéphane Dallongeville and modified by NJ.\r
+YM2612, SN76496 and SH2 cores: MAME devs.\r
+\r
+Special thanks (ideas, valuable information and stuff):\r
+Charles MacDonald, Eke, Exophase, Haze, Lordus, Nemesis,\r
+Pierpaolo Prazzoli, Rokas, Steve Snake, Tasco Deluxe.\r
+\r
+Greets to all the sceners and emu authors out there!\r
+\r
diff --git a/win32/version.h b/win32/version.h
new file mode 100644 (file)
index 0000000..69cf800
--- /dev/null
@@ -0,0 +1,2 @@
+#define VERSION "1.70"\r
+\r