win32 Pico work nearly done
[picodrive.git] / platform / win32 / GenaDrive / Main.cpp
1 #include "app.h"\r
2 #include "version.h"\r
3 #include <crtdbg.h>\r
4 #include <commdlg.h>\r
5 #include "../../common/readpng.h"\r
6 \r
7 char *romname=NULL;\r
8 HWND FrameWnd=NULL;\r
9 RECT FrameRectMy;\r
10 int lock_to_1_1 = 1;\r
11 static HWND PicoSwWnd=NULL, PicoPadWnd=NULL;\r
12 \r
13 int MainWidth=720,MainHeight=480;\r
14 \r
15 static HMENU mmain = 0, mdisplay = 0, mpicohw = 0;\r
16 static int rom_loaded = 0;\r
17 static HBITMAP ppad_bmp = 0;\r
18 static HBITMAP ppage_bmps[6] = { 0, };\r
19 static char rom_name[0x20*3+1];\r
20 \r
21 static void UpdateRect()\r
22 {\r
23   WINDOWINFO wi;\r
24   memset(&wi, 0, sizeof(wi));\r
25   wi.cbSize = sizeof(wi);\r
26   GetWindowInfo(FrameWnd, &wi);\r
27   FrameRectMy = wi.rcClient;\r
28 }\r
29 \r
30 static int extract_rom_name(char *dest, const unsigned char *src, int len)\r
31 {\r
32         char *p = dest, s_old = 0;\r
33         int i;\r
34 \r
35         for (i = len - 1; i >= 0; i--)\r
36         {\r
37                 if (src[i^1] != ' ') break;\r
38         }\r
39         len = i + 1;\r
40 \r
41         for (i = 0; i < len; i++)\r
42         {\r
43                 unsigned char s = src[i^1];\r
44                 if (s == 0x20 && s_old == 0x20) continue;\r
45                 else if (s >= 0x20 && s < 0x7f && s != '%')\r
46                 {\r
47                         *p++ = s;\r
48                 }\r
49                 else\r
50                 {\r
51                         sprintf(p, "%%%02x", s);\r
52                         p += 3;\r
53                 }\r
54                 s_old = s;\r
55         }\r
56         *p = 0;\r
57 \r
58         return p - dest;\r
59 }\r
60 \r
61 \r
62 static HBITMAP png2hb(const char *fname, int is_480)\r
63 {\r
64   BITMAPINFOHEADER bih;\r
65   HBITMAP bmp;\r
66   void *bmem;\r
67   int ret;\r
68 \r
69   bmem = calloc(1, is_480 ? 480*240*3 : 320*240*3);\r
70   if (bmem == NULL) return NULL;\r
71   ret = readpng(bmem, fname, is_480 ? READPNG_480_24 : READPNG_320_24);\r
72   if (ret != 0) {\r
73     free(bmem);\r
74     return NULL;\r
75   }\r
76 \r
77   memset(&bih, 0, sizeof(bih));\r
78   bih.biSize = sizeof(bih);\r
79   bih.biWidth = is_480 ? 480 : 320;\r
80   bih.biHeight = -240;\r
81   bih.biPlanes = 1;\r
82   bih.biBitCount = 24;\r
83   bih.biCompression = BI_RGB;\r
84   bmp = CreateDIBitmap(GetDC(FrameWnd), &bih, CBM_INIT, bmem, (BITMAPINFO *)&bih, 0);\r
85   if (bmp == NULL)\r
86     lprintf("CreateDIBitmap failed with %i", GetLastError());\r
87 \r
88   free(bmem);\r
89   return bmp;\r
90 }\r
91 \r
92 static void PrepareForROM(unsigned char *rom_data)\r
93 {\r
94   int i, ret, show = PicoAHW & PAHW_PICO;\r
95   EnableMenuItem(mmain, 2, MF_BYPOSITION|(show ? MF_ENABLED : MF_GRAYED));\r
96   ShowWindow(PicoPadWnd, show ? SW_SHOWNA : SW_HIDE);\r
97   ShowWindow(PicoSwWnd, show ? SW_SHOWNA : SW_HIDE);\r
98   CheckMenuItem(mpicohw, 1210, show ? MF_CHECKED : MF_UNCHECKED);\r
99   CheckMenuItem(mpicohw, 1211, show ? MF_CHECKED : MF_UNCHECKED);\r
100   PostMessage(FrameWnd, WM_COMMAND, 1220 + PicoPicohw.page, 0);\r
101   DrawMenuBar(FrameWnd);\r
102   InvalidateRect(PicoSwWnd, NULL, 1);\r
103 \r
104   PicoPicohw.pen_pos[0] =\r
105   PicoPicohw.pen_pos[1] = 0x8000;\r
106   picohw_pen_pressed = 0;\r
107 \r
108   ret = extract_rom_name(rom_name, rom_data + 0x150, 0x20);\r
109   if (ret == 0)\r
110     extract_rom_name(rom_name, rom_data + 0x130, 0x20);\r
111 \r
112   if (show)\r
113   {\r
114     char path[MAX_PATH], *p;\r
115     GetModuleFileName(NULL, path, sizeof(path) - 32);\r
116     p = strrchr(path, '\\');\r
117     if (p == NULL) p = path;\r
118     else p++;\r
119     if (ppad_bmp == NULL) {\r
120       strcpy(p, "pico\\pad.png");\r
121       ppad_bmp = png2hb(path, 0);\r
122     }\r
123 \r
124     for (i = 0; i < 6; i++) {\r
125       if (ppage_bmps[i] != NULL) DeleteObject(ppage_bmps[i]);\r
126       sprintf(p, "pico\\%s_%i.png", rom_name, i);\r
127       ppage_bmps[i] = png2hb(path, 1);\r
128     }\r
129   }\r
130 }\r
131 \r
132 static void LoadROM(const char *cmdpath)\r
133 {\r
134   static char rompath[MAX_PATH] = { 0, };\r
135   unsigned char *rom_data_new = NULL;\r
136   unsigned int rom_size = 0;\r
137   pm_file *rom = NULL;\r
138   int oldwait=LoopWait;\r
139   int i, ret;\r
140 \r
141   if (cmdpath) {\r
142     strcpy(rompath, cmdpath + (cmdpath[0] == '\"' ? 1 : 0));\r
143     if (rompath[strlen(rompath)-1] == '\"') rompath[strlen(rompath)-1] = 0;\r
144     if (strlen(rompath) > 4) rom = pm_open(rompath);\r
145   }\r
146 \r
147   if (!rom) {\r
148     OPENFILENAME of; ZeroMemory(&of, sizeof(OPENFILENAME));\r
149     of.lStructSize = sizeof(OPENFILENAME);\r
150     of.lpstrFilter = "ROMs\0*.smd;*.bin;*.gen;*.zip\0";\r
151     of.lpstrFile = rompath; rompath[0] = 0;\r
152     of.nMaxFile = MAX_PATH;\r
153     of.Flags = OFN_FILEMUSTEXIST|OFN_HIDEREADONLY;\r
154     of.hwndOwner = FrameWnd;\r
155     if (!GetOpenFileName(&of)) return;\r
156     rom = pm_open(rompath);\r
157     if (!rom) { error("failed to open ROM"); return; }\r
158   }\r
159 \r
160   ret=PicoCartLoad(rom, &rom_data_new, &rom_size);\r
161   pm_close(rom);\r
162   if (ret) {\r
163     error("failed to load ROM");\r
164     return;\r
165   }\r
166 \r
167   // halt the work thread..\r
168   // just a hack, should've used proper sync. primitives here, but who will use this emu anyway..\r
169   LoopWaiting=0;\r
170   LoopWait=1;\r
171   for (i = 0; LoopWaiting == 0 && i < 10; i++) Sleep(100);\r
172 \r
173   PicoCartUnload();\r
174   PicoCartInsert(rom_data_new, rom_size);\r
175 \r
176   PrepareForROM(rom_data_new);\r
177 \r
178   rom_loaded = 1;\r
179   romname = rompath;\r
180   LoopWait=0;\r
181 }\r
182 \r
183 static int rect_widths[4]  = { 320, 256, 640, 512 };\r
184 static int rect_heights[4] = { 224, 224, 448, 448 };\r
185 \r
186 // Window proc for the frame window:\r
187 static LRESULT CALLBACK WndProc(HWND hwnd,UINT msg,WPARAM wparam,LPARAM lparam)\r
188 {\r
189   POINT pt;\r
190   RECT rc;\r
191   int i;\r
192   switch (msg)\r
193   {\r
194     case WM_CLOSE:   PostQuitMessage(0); return 0;\r
195     case WM_DESTROY: FrameWnd=NULL; break; // Blank handle\r
196     case WM_SIZE:\r
197     case WM_MOVE:\r
198     case WM_SIZING:  UpdateRect(); break;\r
199     case WM_COMMAND:\r
200       switch (LOWORD(wparam))\r
201       {\r
202         case 1000: LoadROM(NULL); break;\r
203         case 1001: PicoReset(); return 0;\r
204         case 1002: PostQuitMessage(0); return 0;\r
205         case 1100:\r
206         case 1101:\r
207         case 1102:\r
208         case 1103:\r
209           LoopWait=1; // another sync hack\r
210           for (i = 0; !LoopWaiting && i < 10; i++) Sleep(10);\r
211           FrameRectMy.right  = FrameRectMy.left + rect_widths[wparam&3];\r
212           FrameRectMy.bottom = FrameRectMy.top  + rect_heights[wparam&3];\r
213           AdjustWindowRect(&FrameRectMy, WS_OVERLAPPEDWINDOW, 1);\r
214           MoveWindow(hwnd, FrameRectMy.left, FrameRectMy.top,\r
215             FrameRectMy.right-FrameRectMy.left, FrameRectMy.bottom-FrameRectMy.top, 1);\r
216           UpdateRect();\r
217           if (HIWORD(wparam) == 0) { // locally sent\r
218             lock_to_1_1=0;\r
219             CheckMenuItem(mdisplay, 1104, MF_UNCHECKED);\r
220           }\r
221           if (rom_loaded) LoopWait=0;\r
222           return 0;\r
223         case 1104:\r
224           lock_to_1_1=!lock_to_1_1;\r
225           CheckMenuItem(mdisplay, 1104, lock_to_1_1 ? MF_CHECKED : MF_UNCHECKED);\r
226           return 0;\r
227         case 1210:\r
228         case 1211:\r
229           i = IsWindowVisible((LOWORD(wparam)&1) ? PicoPadWnd : PicoSwWnd);\r
230           i = !i;\r
231           ShowWindow((LOWORD(wparam)&1) ? PicoPadWnd : PicoSwWnd, i ? SW_SHOWNA : SW_HIDE);\r
232           CheckMenuItem(mpicohw, LOWORD(wparam), i ? MF_CHECKED : MF_UNCHECKED);\r
233           return 0;\r
234         case 1220:\r
235         case 1221:\r
236         case 1222:\r
237         case 1223:\r
238         case 1224:\r
239         case 1225:\r
240         case 1226:\r
241           PicoPicohw.page = LOWORD(wparam) % 10;\r
242           for (i = 0; i < 7; i++)\r
243             CheckMenuItem(mpicohw, 1220 + i, MF_UNCHECKED);\r
244           CheckMenuItem(mpicohw, 1220 + PicoPicohw.page, MF_CHECKED);\r
245           InvalidateRect(PicoSwWnd, NULL, 1);\r
246           return 0;\r
247         case 1300:\r
248           MessageBox(FrameWnd, "PicoDrive v" VERSION " (c) notaz, 2006-2008\n"\r
249               "SVP and Pico demo edition\n\n"\r
250               "Credits:\n"\r
251               "fDave: base code of PicoDrive, GenaDrive (the frontend)\n"\r
252               "Chui: Fame/C\n"\r
253               "NJ: CZ80\n"\r
254               "MAME devs: YM2612 and SN76496 cores\n"\r
255               "Stéphane Dallongeville: Gens code, base of Fame/C (C68K), CZ80\n"\r
256               "Tasco Deluxe: SVP RE work\n"\r
257               "Pierpaolo Prazzoli: info about SSP16 chips\n",\r
258               "About", 0);\r
259           return 0;\r
260       }\r
261       break;\r
262     case WM_TIMER:\r
263       GetCursorPos(&pt);\r
264       GetWindowRect(PicoSwWnd, &rc);\r
265       if (PtInRect(&rc, pt)) break;\r
266       GetWindowRect(PicoPadWnd, &rc);\r
267       if (PtInRect(&rc, pt)) break;\r
268       PicoPicohw.pen_pos[0] |= 0x8000;\r
269       PicoPicohw.pen_pos[1] |= 0x8000;\r
270       picohw_pen_pressed = 0;\r
271       break;\r
272   }\r
273 \r
274   return DefWindowProc(hwnd,msg,wparam,lparam);\r
275 }\r
276 \r
277 static LRESULT CALLBACK PicoSwWndProc(HWND hwnd,UINT msg,WPARAM wparam,LPARAM lparam)\r
278 {\r
279   PAINTSTRUCT ps;\r
280   HDC hdc, hdc2;\r
281 \r
282   switch (msg)\r
283   {\r
284     case WM_CLOSE: return 0;\r
285     case WM_DESTROY: PicoSwWnd=NULL; break;\r
286     case WM_LBUTTONDOWN: picohw_pen_pressed = 1; return 0;\r
287     case WM_LBUTTONUP:   picohw_pen_pressed = 0; return 0;\r
288     case WM_MOUSEMOVE:\r
289       PicoPicohw.pen_pos[0] = 0x03c + LOWORD(lparam) * 2/3;\r
290       PicoPicohw.pen_pos[1] = 0x2f8 + HIWORD(lparam);\r
291       SetTimer(FrameWnd, 100, 1000, NULL);\r
292       break;\r
293     case WM_PAINT:\r
294       hdc = BeginPaint(hwnd, &ps);\r
295       if (ppage_bmps[PicoPicohw.page] == NULL)\r
296       {\r
297         SelectObject(hdc, GetStockObject(DEFAULT_GUI_FONT));\r
298         SetTextColor(hdc, RGB(255, 255, 255));\r
299         SetBkColor(hdc, RGB(0, 0, 0));\r
300         TextOut(hdc, 2,  2, "missing PNGs for", 16);\r
301         TextOut(hdc, 2, 18, rom_name, strlen(rom_name));\r
302       }\r
303       else\r
304       {\r
305         hdc2 = CreateCompatibleDC(GetDC(FrameWnd));\r
306         SelectObject(hdc2, ppage_bmps[PicoPicohw.page]);\r
307         BitBlt(hdc, 0, 0, 480, 240, hdc2, 0, 0, SRCCOPY);\r
308         DeleteDC(hdc2);\r
309       }\r
310       EndPaint(hwnd, &ps);\r
311       return 0;\r
312   }\r
313 \r
314   return DefWindowProc(hwnd,msg,wparam,lparam);\r
315 }\r
316 \r
317 static LRESULT CALLBACK PicoPadWndProc(HWND hwnd,UINT msg,WPARAM wparam,LPARAM lparam)\r
318 {\r
319   PAINTSTRUCT ps;\r
320   HDC hdc, hdc2;\r
321 \r
322   switch (msg)\r
323   {\r
324     case WM_CLOSE: return 0;\r
325     case WM_DESTROY: PicoPadWnd=NULL; break;\r
326     case WM_LBUTTONDOWN: picohw_pen_pressed = 1; return 0;\r
327     case WM_LBUTTONUP:   picohw_pen_pressed = 0; return 0;\r
328     case WM_MOUSEMOVE:\r
329       PicoPicohw.pen_pos[0] = 0x03c + LOWORD(lparam);\r
330       PicoPicohw.pen_pos[1] = 0x1fc + HIWORD(lparam);\r
331       SetTimer(FrameWnd, 100, 1000, NULL);\r
332       break;\r
333     case WM_PAINT:\r
334       if (ppad_bmp == NULL) break;\r
335       hdc = BeginPaint(hwnd, &ps);\r
336       hdc2 = CreateCompatibleDC(GetDC(FrameWnd));\r
337       SelectObject(hdc2, ppad_bmp);\r
338       BitBlt(hdc, 0, 0, 320, 240, hdc2, 0, 0, SRCCOPY);\r
339       EndPaint(hwnd, &ps);\r
340       DeleteDC(hdc2);\r
341       return 0;\r
342   }\r
343 \r
344   return DefWindowProc(hwnd,msg,wparam,lparam);\r
345 }\r
346 \r
347 \r
348 static int FrameInit()\r
349 {\r
350   WNDCLASS wc;\r
351   RECT rect={0,0,0,0};\r
352   HMENU mfile;\r
353   int style=0;\r
354   int left=0,top=0,width=0,height=0;\r
355 \r
356   memset(&wc,0,sizeof(wc));\r
357 \r
358   // Register the window class:\r
359   wc.lpfnWndProc=WndProc;\r
360   wc.hInstance=GetModuleHandle(NULL);\r
361   wc.hCursor=LoadCursor(NULL,IDC_ARROW);\r
362   wc.hbrBackground=CreateSolidBrush(0);\r
363   wc.lpszClassName="PicoMainFrame";\r
364   RegisterClass(&wc);\r
365 \r
366   wc.lpszClassName="PicoSwWnd";\r
367   wc.lpfnWndProc=PicoSwWndProc;\r
368   RegisterClass(&wc);\r
369 \r
370   wc.lpszClassName="PicoPadWnd";\r
371   wc.lpfnWndProc=PicoPadWndProc;\r
372   RegisterClass(&wc);\r
373 \r
374   rect.right =320;//MainWidth;\r
375   rect.bottom=224;//MainHeight;\r
376 \r
377   // Adjust size of windows based on borders:\r
378   style=WS_OVERLAPPEDWINDOW;\r
379   AdjustWindowRect(&rect,style,1);\r
380   width =rect.right-rect.left;\r
381   height=rect.bottom-rect.top;\r
382 \r
383   // Place window in the centre of the screen:\r
384   SystemParametersInfo(SPI_GETWORKAREA,0,&rect,0);\r
385   left=rect.left+rect.right;\r
386   top=rect.top+rect.bottom;\r
387 \r
388   left-=width; left>>=1;\r
389   top-=height; top>>=1;\r
390 \r
391   // Create menu:\r
392   mfile = CreateMenu();\r
393   InsertMenu(mfile, -1, MF_BYPOSITION|MF_STRING, 1000, "&Load ROM");\r
394   InsertMenu(mfile, -1, MF_BYPOSITION|MF_STRING, 1001, "&Reset");\r
395   InsertMenu(mfile, -1, MF_BYPOSITION|MF_STRING, 1002, "E&xit");\r
396   mdisplay = CreateMenu();\r
397   InsertMenu(mdisplay, -1, MF_BYPOSITION|MF_STRING, 1100, "320x224");\r
398   InsertMenu(mdisplay, -1, MF_BYPOSITION|MF_STRING, 1101, "256x224");\r
399   InsertMenu(mdisplay, -1, MF_BYPOSITION|MF_STRING, 1102, "640x448");\r
400   InsertMenu(mdisplay, -1, MF_BYPOSITION|MF_STRING, 1103, "512x448");\r
401   InsertMenu(mdisplay, -1, MF_BYPOSITION|MF_STRING, 1104, "Lock to 1:1");\r
402   mpicohw = CreateMenu();\r
403   InsertMenu(mpicohw, -1, MF_BYPOSITION|MF_STRING, 1210, "Show &Storyware");\r
404   InsertMenu(mpicohw, -1, MF_BYPOSITION|MF_STRING, 1211, "Show &Drawing pad");\r
405   InsertMenu(mpicohw, -1, MF_BYPOSITION|MF_SEPARATOR, 0, NULL);\r
406   InsertMenu(mpicohw, -1, MF_BYPOSITION|MF_STRING, 1220, "Title page (&0)");\r
407   InsertMenu(mpicohw, -1, MF_BYPOSITION|MF_STRING, 1221, "Page &1");\r
408   InsertMenu(mpicohw, -1, MF_BYPOSITION|MF_STRING, 1222, "Page &2");\r
409   InsertMenu(mpicohw, -1, MF_BYPOSITION|MF_STRING, 1223, "Page &3");\r
410   InsertMenu(mpicohw, -1, MF_BYPOSITION|MF_STRING, 1224, "Page &4");\r
411   InsertMenu(mpicohw, -1, MF_BYPOSITION|MF_STRING, 1225, "Page &5");\r
412   InsertMenu(mpicohw, -1, MF_BYPOSITION|MF_STRING, 1226, "Page &6");\r
413   mmain = CreateMenu();\r
414   InsertMenu(mmain, -1, MF_BYPOSITION|MF_STRING|MF_POPUP, (UINT_PTR) mfile,    "&File");\r
415   InsertMenu(mmain, -1, MF_BYPOSITION|MF_STRING|MF_POPUP, (UINT_PTR) mdisplay, "&Display");\r
416   InsertMenu(mmain, -1, MF_BYPOSITION|MF_STRING|MF_POPUP, (UINT_PTR) mpicohw,  "&Pico");\r
417   EnableMenuItem(mmain, 2, MF_BYPOSITION|MF_GRAYED);\r
418 //  InsertMenu(mmain, -1, MF_BYPOSITION|MF_STRING|MF_POPUP, 1200, "&Config");\r
419   InsertMenu(mmain, -1, MF_BYPOSITION|MF_STRING, 1300, "&About");\r
420 \r
421   // Create the window:\r
422   FrameWnd=CreateWindow("PicoMainFrame","PicoDrive " VERSION,style|WS_VISIBLE,\r
423     left,top,width,height,NULL,mmain,NULL,NULL);\r
424 \r
425   CheckMenuItem(mdisplay, 1104, lock_to_1_1 ? MF_CHECKED : MF_UNCHECKED);\r
426   ShowWindow(FrameWnd, SW_NORMAL);\r
427   UpdateWindow(FrameWnd);\r
428   UpdateRect();\r
429 \r
430   // create Pico windows\r
431   style = WS_OVERLAPPED|WS_CAPTION|WS_BORDER;\r
432   rect.left=rect.top=0;\r
433   rect.right =320;\r
434   rect.bottom=224;\r
435 \r
436   AdjustWindowRect(&rect,style,1);\r
437   width =rect.right-rect.left;\r
438   height=rect.bottom-rect.top;\r
439 \r
440   left += 326;\r
441   PicoSwWnd=CreateWindow("PicoSwWnd","Storyware",style,\r
442     left,top,width+160,height,FrameWnd,NULL,NULL,NULL);\r
443 \r
444   top += 266;\r
445   PicoPadWnd=CreateWindow("PicoPadWnd","Drawing Pad",style,\r
446     left,top,width,height,FrameWnd,NULL,NULL,NULL);\r
447 \r
448   return 0;\r
449 }\r
450 \r
451 // --------------------\r
452 \r
453 static DWORD WINAPI ThreadCode(void *)\r
454 {\r
455   LoopCode();\r
456   return 0;\r
457 }\r
458 \r
459 int WINAPI WinMain(HINSTANCE,HINSTANCE,LPSTR cmdline,int)\r
460 {\r
461   MSG msg;\r
462   int ret=0;\r
463   DWORD tid=0;\r
464   HANDLE thread=NULL;\r
465 \r
466   FrameInit();\r
467   ret=LoopInit(); if (ret) goto end0;\r
468 \r
469   // Make another thread to run LoopCode():\r
470   LoopQuit=0;\r
471   LoopWait=1; // wait for ROM to be loaded\r
472   thread=CreateThread(NULL,0,ThreadCode,NULL,0,&tid);\r
473 \r
474   LoadROM(cmdline);\r
475 \r
476   // Main window loop:\r
477   for (;;)\r
478   {\r
479     GetMessage(&msg,NULL,0,0);\r
480     if (msg.message==WM_QUIT) break;\r
481 \r
482     TranslateMessage(&msg);\r
483     DispatchMessage(&msg);\r
484   }\r
485 \r
486   // Signal thread to quit and wait for it to exit:\r
487   LoopQuit=1; WaitForSingleObject(thread,5000);\r
488   CloseHandle(thread); thread=NULL;\r
489 \r
490 end0:\r
491   LoopExit();\r
492   DestroyWindow(FrameWnd);\r
493 \r
494   _CrtDumpMemoryLeaks();\r
495   return 0;\r
496 }\r
497 \r
498 extern void error(char *text)\r
499 {\r
500   MessageBox(FrameWnd, text, "Error", 0);\r
501 }\r
502 \r