author | dominik |
Mon, 16 Aug 2004 13:31:18 +0000 | |
changeset 63 | 53cc59601b3c |
parent 49 | 8dd4b23924c1 |
child 135 | 638fb31434eb |
permissions | -rw-r--r-- |
0 | 1 |
#include "stdafx.h" |
2 |
#include "ttd.h" |
|
3 |
#include "gfx.h" |
|
4 |
#include "sound.h" |
|
5 |
#include "window.h" |
|
6 |
#include <windows.h> |
|
7 |
#include <mmsystem.h> |
|
8 |
#include "hal.h" |
|
9 |
#include <winnt.h> |
|
10 |
#include <wininet.h> |
|
11 |
#include <io.h> |
|
12 |
#include <fcntl.h> |
|
13 |
||
14 |
#define SMART_PALETTE_ANIM |
|
15 |
||
16 |
static struct { |
|
17 |
HWND main_wnd; |
|
18 |
HBITMAP dib_sect; |
|
19 |
void *bitmap_bits; |
|
20 |
void *buffer_bits; |
|
21 |
void *alloced_bits; |
|
22 |
HPALETTE gdi_palette; |
|
23 |
int width,height; |
|
24 |
int width_org, height_org; |
|
25 |
bool cursor_visible; |
|
26 |
bool switch_driver; |
|
27 |
bool fullscreen; |
|
28 |
bool double_size; |
|
29 |
bool has_focus; |
|
30 |
bool running; |
|
31 |
} _wnd; |
|
32 |
||
33 |
static HINSTANCE _inst; |
|
34 |
static bool _has_console; |
|
35 |
||
36 |
#if defined(MINGW32) || defined(__CYGWIN__) |
|
37 |
#define __TIMESTAMP__ __DATE__ __TIME__ |
|
38 |
#endif |
|
39 |
||
40 |
#ifdef WIN32_ENABLE_DIRECTMUSIC_SUPPORT |
|
41 |
extern const HalMusicDriver _dmusic_midi_driver; |
|
42 |
#endif |
|
43 |
||
44 |
static void MakePalette() |
|
45 |
{ |
|
46 |
LOGPALETTE *pal; |
|
47 |
int i; |
|
48 |
byte *b; |
|
49 |
||
50 |
pal = alloca(sizeof(LOGPALETTE) + (256-1) * sizeof(PALETTEENTRY)); |
|
51 |
||
52 |
pal->palVersion = 0x300; |
|
53 |
pal->palNumEntries = 256; |
|
54 |
||
55 |
for(i=0,b=_cur_palette; i!=256;i++,b+=3) { |
|
56 |
pal->palPalEntry[i].peRed = b[0]; |
|
57 |
pal->palPalEntry[i].peGreen = b[1]; |
|
58 |
pal->palPalEntry[i].peBlue = b[2]; |
|
59 |
pal->palPalEntry[i].peFlags = 0; |
|
60 |
||
61 |
} |
|
62 |
_wnd.gdi_palette = CreatePalette(pal); |
|
63 |
if (_wnd.gdi_palette == NULL) |
|
64 |
error("CreatePalette failed!\n"); |
|
65 |
} |
|
66 |
||
67 |
static void UpdatePalette(HDC dc, uint start, uint count) |
|
68 |
{ |
|
69 |
RGBQUAD rgb[256]; |
|
70 |
uint i; |
|
71 |
byte *b; |
|
72 |
||
73 |
for(i=0,b = _cur_palette + start*3; i!=count; i++,b+=3) { |
|
74 |
rgb[i].rgbRed = b[0]; |
|
75 |
rgb[i].rgbGreen = b[1]; |
|
76 |
rgb[i].rgbBlue = b[2]; |
|
77 |
rgb[i].rgbReserved = 0; |
|
78 |
} |
|
79 |
||
80 |
SetDIBColorTable(dc, start, count, rgb); |
|
81 |
} |
|
82 |
||
83 |
static bool MyShowCursor(bool show) |
|
84 |
{ |
|
85 |
if (_wnd.cursor_visible == show) |
|
86 |
return show; |
|
87 |
||
88 |
_wnd.cursor_visible = show; |
|
89 |
ShowCursor(show); |
|
90 |
||
91 |
return !show; |
|
92 |
} |
|
93 |
||
94 |
typedef struct { |
|
95 |
byte vk_from; |
|
96 |
byte vk_count; |
|
97 |
byte map_to; |
|
98 |
} VkMapping; |
|
99 |
||
100 |
#define AS(x,z) {x,0,z} |
|
101 |
#define AM(x,y,z,w) {x,y-x,z} |
|
102 |
||
103 |
static const VkMapping _vk_mapping[] = { |
|
104 |
// Pageup stuff + up/down |
|
105 |
AM(VK_PRIOR,VK_DOWN, WKC_PAGEUP, WKC_DOWN), |
|
106 |
// Map letters & digits |
|
107 |
AM('A','Z','A','Z'), |
|
108 |
AM('0','9','0','9'), |
|
109 |
||
110 |
AS(VK_ESCAPE, WKC_ESC), |
|
111 |
AS(VK_BACK, WKC_BACKSPACE), |
|
112 |
AM(VK_INSERT,VK_DELETE,WKC_INSERT, WKC_DELETE), |
|
113 |
||
114 |
AS(VK_SPACE, WKC_SPACE), |
|
115 |
AS(VK_RETURN, WKC_RETURN), |
|
116 |
AS(VK_TAB, WKC_TAB), |
|
117 |
||
118 |
// Function keys |
|
119 |
AM(VK_F1, VK_F12, WKC_F1, WKC_F12), |
|
120 |
||
121 |
// Numeric part. |
|
122 |
// What is the virtual keycode for numeric enter?? |
|
123 |
AM(VK_NUMPAD0,VK_NUMPAD9, WKC_NUM_0, WKC_NUM_9), |
|
124 |
AS(VK_DIVIDE, WKC_NUM_DIV), |
|
125 |
AS(VK_MULTIPLY, WKC_NUM_MUL), |
|
126 |
AS(VK_SUBTRACT, WKC_NUM_MINUS), |
|
127 |
AS(VK_ADD, WKC_NUM_PLUS), |
|
128 |
AS(VK_DECIMAL, WKC_NUM_DECIMAL), |
|
129 |
{0} |
|
130 |
}; |
|
131 |
||
132 |
static uint MapWindowsKey(uint key) |
|
133 |
{ |
|
134 |
const VkMapping *map = _vk_mapping - 1; |
|
135 |
uint from; |
|
136 |
do { |
|
137 |
map++; |
|
138 |
from = map->vk_from; |
|
139 |
if (from == 0) return 0; // Unknown key pressed. |
|
140 |
} while ((uint)(key - from) > map->vk_count); |
|
141 |
||
142 |
key = key - from + map->map_to; |
|
143 |
if (GetAsyncKeyState(VK_SHIFT)<0) key |= WKC_SHIFT; |
|
144 |
if (GetAsyncKeyState(VK_CONTROL)<0) key |= WKC_CTRL; |
|
145 |
if (GetAsyncKeyState(VK_MENU)<0) key |= WKC_ALT; |
|
146 |
return key; |
|
147 |
} |
|
148 |
||
149 |
static void MakeWindow(bool full_screen); |
|
150 |
static bool AllocateDibSection(int w, int h); |
|
151 |
||
152 |
static void ClientSizeChanged(int w, int h) |
|
153 |
{ |
|
154 |
if (_wnd.double_size) { w >>= 1; h >>= 1; } |
|
155 |
||
156 |
// allocate new dib section of the new size |
|
157 |
if (AllocateDibSection(w, h)) { |
|
158 |
// mark all palette colors dirty |
|
159 |
_pal_first_dirty = 0; |
|
160 |
_pal_last_dirty = 255; |
|
161 |
GameSizeChanged(); |
|
162 |
||
163 |
// redraw screen |
|
164 |
if (_wnd.running) { |
|
165 |
_screen.dst_ptr = _wnd.buffer_bits; |
|
166 |
UpdateWindows(); |
|
167 |
} |
|
168 |
} |
|
169 |
} |
|
170 |
||
171 |
static LRESULT CALLBACK WndProcGdi(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) |
|
172 |
{ |
|
173 |
switch(msg) { |
|
174 |
case WM_PAINT: { |
|
175 |
PAINTSTRUCT ps; |
|
176 |
HDC dc,dc2; |
|
177 |
HBITMAP old_bmp; |
|
178 |
HPALETTE old_palette; |
|
179 |
BeginPaint(hwnd, &ps); |
|
180 |
dc = ps.hdc; |
|
181 |
dc2 = CreateCompatibleDC(dc); |
|
182 |
old_bmp = SelectObject(dc2, _wnd.dib_sect); |
|
183 |
old_palette = SelectPalette(dc, _wnd.gdi_palette, FALSE); |
|
184 |
||
185 |
if (_pal_last_dirty != -1) { |
|
186 |
UpdatePalette(dc2, _pal_first_dirty, _pal_last_dirty - _pal_first_dirty + 1); |
|
187 |
_pal_last_dirty = -1; |
|
188 |
} |
|
189 |
||
190 |
BitBlt(dc, 0, 0, _wnd.width, _wnd.height, dc2, 0, 0, SRCCOPY); |
|
191 |
SelectPalette(dc, old_palette, TRUE); |
|
192 |
SelectObject(dc2, old_bmp); |
|
193 |
DeleteDC(dc2); |
|
194 |
EndPaint(hwnd, &ps); |
|
195 |
} |
|
196 |
return 0; |
|
197 |
||
198 |
case WM_PALETTECHANGED: |
|
199 |
if ((HWND)wParam == hwnd) |
|
200 |
return 0; |
|
201 |
// FALL THROUGH |
|
202 |
case WM_QUERYNEWPALETTE: { |
|
203 |
HDC hDC = GetWindowDC(hwnd); |
|
204 |
HPALETTE hOldPalette = SelectPalette(hDC, _wnd.gdi_palette, FALSE); |
|
205 |
UINT nChanged = RealizePalette(hDC); |
|
206 |
SelectPalette(hDC, hOldPalette, TRUE); |
|
207 |
ReleaseDC(hwnd, hDC); |
|
208 |
if (nChanged) |
|
209 |
InvalidateRect(hwnd, NULL, FALSE); |
|
210 |
return 0; |
|
211 |
} |
|
212 |
||
213 |
case WM_CLOSE: |
|
214 |
AskExitGame(); |
|
215 |
return 0; |
|
216 |
||
217 |
case WM_LBUTTONDOWN: |
|
218 |
SetCapture(hwnd); |
|
219 |
_left_button_down = true; |
|
220 |
return 0; |
|
221 |
||
222 |
case WM_LBUTTONUP: |
|
223 |
ReleaseCapture(); |
|
224 |
_left_button_down = false; |
|
225 |
_left_button_clicked = false; |
|
226 |
return 0; |
|
227 |
||
228 |
case WM_RBUTTONDOWN: |
|
229 |
SetCapture(hwnd); |
|
230 |
_right_button_down = true; |
|
231 |
_right_button_clicked = true; |
|
232 |
return 0; |
|
233 |
||
234 |
case WM_RBUTTONUP: |
|
235 |
ReleaseCapture(); |
|
236 |
_right_button_down = false; |
|
237 |
return 0; |
|
238 |
||
239 |
case WM_MOUSEMOVE: { |
|
240 |
int x = (int16)LOWORD(lParam); |
|
241 |
int y = (int16)HIWORD(lParam); |
|
242 |
POINT pt; |
|
243 |
||
244 |
if (_wnd.double_size) { |
|
245 |
x >>= 1; |
|
246 |
y >>= 1; |
|
247 |
} |
|
248 |
||
249 |
if (_cursor.fix_at) { |
|
250 |
int dx = x - _cursor.pos.x; |
|
251 |
int dy = y - _cursor.pos.y; |
|
252 |
if (dx != 0 || dy != 0) { |
|
253 |
_cursor.delta.x += dx; |
|
254 |
_cursor.delta.y += dy; |
|
255 |
||
256 |
pt.x = _cursor.pos.x; |
|
257 |
pt.y = _cursor.pos.y; |
|
258 |
||
259 |
if (_wnd.double_size) { |
|
260 |
pt.x *= 2; |
|
261 |
pt.y *= 2; |
|
262 |
} |
|
263 |
ClientToScreen(hwnd, &pt); |
|
264 |
SetCursorPos(pt.x, pt.y); |
|
265 |
} |
|
266 |
} else { |
|
267 |
_cursor.delta.x += x - _cursor.pos.x; |
|
268 |
_cursor.delta.y += y - _cursor.pos.y; |
|
269 |
_cursor.pos.x = x; |
|
270 |
_cursor.pos.y = y; |
|
271 |
_cursor.dirty = true; |
|
272 |
} |
|
273 |
MyShowCursor(false); |
|
274 |
return 0; |
|
275 |
} |
|
276 |
||
277 |
case WM_KEYDOWN: |
|
278 |
_pressed_key = MapWindowsKey(wParam) << 16; |
|
279 |
if ((_pressed_key>>16) == ('D' | WKC_CTRL) && !_wnd.fullscreen) { |
|
280 |
_double_size ^= 1; |
|
281 |
_wnd.double_size = _double_size; |
|
282 |
ClientSizeChanged(_wnd.width, _wnd.height); |
|
283 |
MarkWholeScreenDirty(); |
|
284 |
} |
|
285 |
break; |
|
286 |
||
287 |
case WM_CHAR: |
|
288 |
_pressed_key |= wParam; |
|
289 |
break; |
|
290 |
||
291 |
case WM_SYSKEYDOWN: |
|
292 |
switch(wParam) { |
|
293 |
case VK_RETURN: |
|
294 |
MakeWindow(!_wnd.fullscreen); |
|
295 |
return 0; |
|
296 |
default: |
|
297 |
_pressed_key = MapWindowsKey(wParam) << 16; |
|
298 |
break; |
|
299 |
} |
|
300 |
break; |
|
301 |
case WM_NCMOUSEMOVE: |
|
302 |
MyShowCursor(true); |
|
303 |
return 0; |
|
304 |
||
305 |
case WM_SIZE: { |
|
306 |
if (wParam != SIZE_MINIMIZED) { |
|
307 |
ClientSizeChanged(LOWORD(lParam), HIWORD(lParam)); |
|
308 |
} |
|
309 |
return 0; |
|
310 |
} |
|
311 |
case WM_SIZING: { |
|
312 |
RECT* r = (RECT*)lParam; |
|
313 |
RECT r2; |
|
314 |
int w, h; |
|
315 |
||
316 |
SetRect(&r2, 0, 0, 0, 0); |
|
317 |
AdjustWindowRect(&r2, GetWindowLong(hwnd, GWL_STYLE), FALSE); |
|
318 |
||
319 |
w = r->right - r->left - (r2.right - r2.left); |
|
320 |
h = r->bottom - r->top - (r2.bottom - r2.top); |
|
321 |
if (_wnd.double_size) { w >>= 1; h >>= 1; } |
|
322 |
w = clamp(w & ~0x7, 64, MAX_SCREEN_WIDTH); |
|
323 |
h = clamp(h & ~0x7, 64, MAX_SCREEN_HEIGHT); |
|
324 |
if (_wnd.double_size) { w <<= 1; h <<= 1; } |
|
325 |
SetRect(&r2, 0, 0, w, h); |
|
326 |
||
327 |
AdjustWindowRect(&r2, GetWindowLong(hwnd, GWL_STYLE), FALSE); |
|
328 |
w = r2.right - r2.left; |
|
329 |
h = r2.bottom - r2.top; |
|
330 |
||
331 |
switch (wParam) { |
|
332 |
case WMSZ_BOTTOM: |
|
333 |
r->bottom = r->top + h; |
|
334 |
break; |
|
335 |
case WMSZ_BOTTOMLEFT: |
|
336 |
r->bottom = r->top + h; |
|
337 |
r->left = r->right - w; |
|
338 |
break; |
|
339 |
case WMSZ_BOTTOMRIGHT: |
|
340 |
r->bottom = r->top + h; |
|
341 |
r->right = r->left + w; |
|
342 |
break; |
|
343 |
case WMSZ_LEFT: |
|
344 |
r->left = r->right - w; |
|
345 |
break; |
|
346 |
case WMSZ_RIGHT: |
|
347 |
r->right = r->left + w; |
|
348 |
break; |
|
349 |
case WMSZ_TOP: |
|
350 |
r->top = r->bottom - h; |
|
351 |
break; |
|
352 |
case WMSZ_TOPLEFT: |
|
353 |
r->top = r->bottom - h; |
|
354 |
r->left = r->right - w; |
|
355 |
break; |
|
356 |
case WMSZ_TOPRIGHT: |
|
357 |
r->top = r->bottom - h; |
|
358 |
r->right = r->left + w; |
|
359 |
break; |
|
360 |
} |
|
361 |
return TRUE; |
|
362 |
} |
|
363 |
||
364 |
// needed for wheel |
|
365 |
#if !defined(WM_MOUSEWHEEL) |
|
366 |
# define WM_MOUSEWHEEL 0x020A |
|
367 |
#endif //WM_MOUSEWHEEL |
|
368 |
#if !defined(GET_WHEEL_DELTA_WPARAM) |
|
369 |
# define GET_WHEEL_DELTA_WPARAM(wparam) ((short)HIWORD(wparam)) |
|
370 |
#endif //GET_WHEEL_DELTA_WPARAM |
|
371 |
||
372 |
case WM_MOUSEWHEEL: { |
|
373 |
int delta = GET_WHEEL_DELTA_WPARAM(wParam); |
|
374 |
if (delta < 0) { |
|
375 |
_cursor.wheel++; |
|
376 |
} else if (delta > 0) { |
|
377 |
_cursor.wheel--; |
|
378 |
} |
|
379 |
return 0; |
|
380 |
} |
|
381 |
||
382 |
case WM_ACTIVATEAPP: |
|
383 |
_wnd.has_focus = (bool)wParam; |
|
384 |
break; |
|
385 |
} |
|
386 |
return DefWindowProc(hwnd, msg, wParam, lParam); |
|
387 |
} |
|
388 |
||
389 |
static void RegisterWndClass() |
|
390 |
{ |
|
391 |
static bool registered; |
|
392 |
if (!registered) { |
|
393 |
HINSTANCE hinst = GetModuleHandle(NULL); |
|
394 |
WNDCLASS wnd = { |
|
395 |
0, |
|
396 |
WndProcGdi, |
|
397 |
0, |
|
398 |
0, |
|
399 |
hinst, |
|
400 |
LoadIcon(hinst, MAKEINTRESOURCE(100)), |
|
401 |
LoadCursor(NULL, IDC_ARROW), |
|
402 |
0, |
|
403 |
0, |
|
404 |
"TTD" |
|
405 |
}; |
|
406 |
registered = true; |
|
407 |
if (!RegisterClass(&wnd)) |
|
408 |
error("RegisterClass failed"); |
|
409 |
} |
|
410 |
} |
|
411 |
||
412 |
static void MakeWindow(bool full_screen) |
|
413 |
{ |
|
414 |
_fullscreen = full_screen; |
|
415 |
||
416 |
_wnd.double_size = _double_size && !full_screen; |
|
417 |
||
418 |
// recreate window? |
|
419 |
if ((full_screen|_wnd.fullscreen) && _wnd.main_wnd) { |
|
420 |
DestroyWindow(_wnd.main_wnd); |
|
421 |
_wnd.main_wnd = 0; |
|
422 |
} |
|
423 |
||
424 |
if (full_screen) { |
|
425 |
DEVMODE settings; |
|
426 |
memset(&settings, 0, sizeof(DEVMODE)); |
|
427 |
settings.dmSize = sizeof(DEVMODE); |
|
428 |
settings.dmFields = DM_PELSWIDTH | DM_PELSHEIGHT; |
|
429 |
||
430 |
if (_fullscreen_bpp) { |
|
431 |
settings.dmBitsPerPel = _fullscreen_bpp; |
|
432 |
settings.dmFields |= DM_BITSPERPEL; |
|
433 |
} |
|
434 |
settings.dmPelsWidth = _wnd.width_org; |
|
435 |
settings.dmPelsHeight = _wnd.height_org; |
|
436 |
if ((settings.dmDisplayFrequency = _display_hz) != 0) |
|
437 |
settings.dmFields |= DM_DISPLAYFREQUENCY; |
|
438 |
if ( !ChangeDisplaySettings(&settings, CDS_FULLSCREEN) == DISP_CHANGE_SUCCESSFUL ) { |
|
439 |
MakeWindow(false); |
|
440 |
return; |
|
441 |
} |
|
442 |
} else if (_wnd.fullscreen) { |
|
443 |
// restore display? |
|
444 |
ChangeDisplaySettings(NULL, 0); |
|
445 |
} |
|
446 |
||
447 |
{ |
|
448 |
RECT r; |
|
449 |
uint style; |
|
450 |
int x, y, w, h; |
|
451 |
||
452 |
if ((_wnd.fullscreen=full_screen) != false) { |
|
453 |
style = WS_POPUP | WS_VISIBLE; |
|
454 |
SetRect(&r, 0, 0, _wnd.width_org, _wnd.height_org); |
|
455 |
} else { |
|
456 |
style = WS_OVERLAPPEDWINDOW | WS_VISIBLE; |
|
457 |
SetRect(&r, 0, 0, _wnd.width, _wnd.height); |
|
458 |
} |
|
459 |
||
460 |
AdjustWindowRect(&r, style, FALSE); |
|
461 |
w = r.right - r.left; |
|
462 |
h = r.bottom - r.top; |
|
463 |
x = (GetSystemMetrics(SM_CXSCREEN)-w)>>1; |
|
464 |
y = (GetSystemMetrics(SM_CYSCREEN)-h)>>1; |
|
465 |
||
466 |
if (_wnd.main_wnd) { |
|
467 |
SetWindowPos(_wnd.main_wnd, 0, x, y, w, h, SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_NOZORDER); |
|
468 |
} else { |
|
469 |
_wnd.main_wnd = CreateWindow("TTD", "OpenTTD", style, x, y, w, h, 0, 0, _inst, 0); |
|
470 |
if (_wnd.main_wnd == NULL) |
|
471 |
error("CreateWindow failed"); |
|
472 |
} |
|
473 |
} |
|
474 |
} |
|
475 |
||
476 |
static bool AllocateDibSection(int w, int h) |
|
477 |
{ |
|
478 |
BITMAPINFO *bi; |
|
479 |
HDC dc; |
|
480 |
||
481 |
w = clamp(w & ~7, 64, MAX_SCREEN_WIDTH); |
|
482 |
h = clamp(h & ~7, 64, MAX_SCREEN_HEIGHT); |
|
483 |
||
484 |
if (w == _screen.width && h == _screen.height) |
|
485 |
return false; |
|
486 |
||
487 |
_screen.width = _screen.pitch = w; |
|
488 |
_screen.height = h; |
|
489 |
||
490 |
if (_wnd.alloced_bits) { |
|
491 |
free(_wnd.alloced_bits); |
|
492 |
_wnd.alloced_bits = NULL; |
|
493 |
} |
|
494 |
||
495 |
bi = alloca(sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD)*256); |
|
496 |
memset(bi, 0, sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD)*256); |
|
497 |
bi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER); |
|
498 |
||
499 |
{ |
|
500 |
if (_wnd.double_size) { |
|
501 |
_wnd.alloced_bits = _wnd.buffer_bits = (byte*)malloc(w * h); |
|
502 |
w *= 2; |
|
503 |
h *= 2; |
|
504 |
} |
|
505 |
||
506 |
bi->bmiHeader.biWidth = _wnd.width = w; |
|
507 |
bi->bmiHeader.biHeight = -(_wnd.height = h); |
|
508 |
} |
|
509 |
||
510 |
bi->bmiHeader.biPlanes = 1; |
|
511 |
bi->bmiHeader.biBitCount = 8; |
|
512 |
bi->bmiHeader.biCompression = BI_RGB; |
|
513 |
||
514 |
if (_wnd.dib_sect) |
|
515 |
DeleteObject(_wnd.dib_sect); |
|
516 |
||
517 |
dc = GetDC(0); |
|
518 |
_wnd.dib_sect = CreateDIBSection(dc, bi, DIB_RGB_COLORS, &_wnd.bitmap_bits, NULL, 0); |
|
519 |
if (_wnd.dib_sect == NULL) |
|
520 |
error("CreateDIBSection failed"); |
|
521 |
ReleaseDC(0, dc); |
|
522 |
||
523 |
if (!_wnd.double_size) |
|
524 |
_wnd.buffer_bits = _wnd.bitmap_bits; |
|
525 |
||
526 |
return true; |
|
527 |
} |
|
528 |
||
529 |
static const uint16 default_resolutions[][2] = { |
|
530 |
{640,480}, |
|
531 |
{800,600}, |
|
532 |
{1024,768}, |
|
533 |
{1152,864}, |
|
534 |
{1280,960}, |
|
535 |
{1280,1024}, |
|
536 |
{1400,1050}, |
|
537 |
{1600,1200}, |
|
538 |
}; |
|
539 |
||
540 |
static void FindResolutions() |
|
541 |
{ |
|
542 |
int i = 0, n = 0; |
|
543 |
DEVMODE dm; |
|
544 |
||
545 |
while (EnumDisplaySettings(NULL, i++, &dm)) { |
|
546 |
if (dm.dmBitsPerPel == 8 && |
|
547 |
IS_INT_INSIDE(dm.dmPelsWidth, 640, MAX_SCREEN_WIDTH+1) && |
|
548 |
IS_INT_INSIDE(dm.dmPelsHeight, 480, MAX_SCREEN_HEIGHT+1) && |
|
549 |
(n == 0 || _resolutions[n-1][0] != dm.dmPelsWidth || _resolutions[n-1][1] != dm.dmPelsHeight)) { |
|
550 |
_resolutions[n][0] = dm.dmPelsWidth; |
|
551 |
_resolutions[n][1] = dm.dmPelsHeight; |
|
552 |
if (++n == sizeof(_resolutions) / (sizeof(uint16)*2)) break; |
|
553 |
} |
|
554 |
} |
|
555 |
||
556 |
if (n == 0) { |
|
557 |
memcpy(_resolutions, default_resolutions, sizeof(default_resolutions)); |
|
558 |
n = 6; |
|
559 |
} |
|
560 |
||
561 |
_num_resolutions = n; |
|
562 |
} |
|
563 |
||
564 |
||
8
9ab81ef450f2
(svn r9) Fixed a couple of warnings and minor coding issues
dominik
parents:
0
diff
changeset
|
565 |
static const char *Win32GdiStart(char **parm) |
0 | 566 |
{ |
567 |
memset(&_wnd, 0, sizeof(_wnd)); |
|
568 |
_wnd.cursor_visible = true; |
|
569 |
||
570 |
RegisterWndClass(); |
|
571 |
||
572 |
MakePalette(); |
|
573 |
||
574 |
FindResolutions(); |
|
575 |
||
576 |
// fullscreen uses those |
|
577 |
_wnd.width_org = _cur_resolution[0]; |
|
578 |
_wnd.height_org = _cur_resolution[1]; |
|
579 |
||
580 |
AllocateDibSection(_cur_resolution[0], _cur_resolution[1]); |
|
581 |
MarkWholeScreenDirty(); |
|
582 |
||
583 |
MakeWindow(_fullscreen); |
|
584 |
||
585 |
return NULL; |
|
586 |
} |
|
587 |
||
588 |
static void Win32GdiStop() |
|
589 |
{ |
|
590 |
if ( _wnd.fullscreen ) { |
|
591 |
ChangeDisplaySettings(NULL, 0); |
|
592 |
} |
|
593 |
MyShowCursor(true); |
|
594 |
DeleteObject(_wnd.gdi_palette); |
|
595 |
DeleteObject(_wnd.dib_sect); |
|
596 |
DestroyWindow(_wnd.main_wnd); |
|
597 |
} |
|
598 |
||
599 |
// simple upscaler by 2 |
|
600 |
static void filter(int left, int top, int width, int height) |
|
601 |
{ |
|
602 |
uint p = _screen.pitch; |
|
603 |
byte *s = (byte*)_wnd.buffer_bits + top * p + left; |
|
604 |
byte *d = (byte*)_wnd.bitmap_bits + top * p * 4 + left * 2; |
|
605 |
int i; |
|
606 |
||
607 |
while (height) { |
|
608 |
for(i=0; i!=width; i++) { |
|
609 |
d[i*2] = d[i*2+1] = d[i*2+p*2] = d[i*2+1+p*2] = s[i]; |
|
610 |
} |
|
611 |
s += p; |
|
612 |
d += p * 4; |
|
613 |
height--; |
|
614 |
} |
|
615 |
} |
|
616 |
||
617 |
static void Win32GdiMakeDirty(int left, int top, int width, int height) |
|
618 |
{ |
|
619 |
RECT r = {left, top, left+width, top+height}; |
|
620 |
if (_wnd.double_size) { |
|
621 |
filter(left, top, width, height); |
|
622 |
//filter(0, 0, 640, 480); |
|
623 |
r.left *= 2;r.top *= 2;r.right *= 2;r.bottom *= 2; |
|
624 |
} |
|
625 |
InvalidateRect(_wnd.main_wnd, &r, FALSE); |
|
626 |
} |
|
627 |
||
628 |
static void CheckPaletteAnim() |
|
629 |
{ |
|
630 |
if (_pal_last_dirty == -1) |
|
631 |
return; |
|
632 |
InvalidateRect(_wnd.main_wnd, NULL, FALSE); |
|
633 |
} |
|
634 |
||
635 |
static int Win32GdiMainLoop() |
|
636 |
{ |
|
637 |
MSG mesg; |
|
638 |
uint32 next_tick = GetTickCount() + 30, cur_ticks; |
|
639 |
||
640 |
_wnd.running = true; |
|
641 |
||
642 |
while(true) { |
|
643 |
while (PeekMessage(&mesg, NULL, 0, 0, PM_REMOVE)) { |
|
644 |
InteractiveRandom(); // randomness |
|
645 |
TranslateMessage(&mesg); |
|
646 |
DispatchMessage(&mesg); |
|
647 |
} |
|
648 |
if (_exit_game) return ML_QUIT; |
|
649 |
if (_wnd.switch_driver) return ML_SWITCHDRIVER; |
|
650 |
||
651 |
#if defined(_DEBUG) |
|
652 |
if (_wnd.has_focus && GetAsyncKeyState(VK_SHIFT) < 0) { |
|
653 |
#else |
|
654 |
if (_wnd.has_focus && GetAsyncKeyState(VK_TAB) < 0) { |
|
655 |
#endif |
|
656 |
_fast_forward |= 2; |
|
657 |
} else if (_fast_forward&2) { |
|
658 |
_fast_forward = 0; |
|
659 |
} |
|
660 |
||
661 |
||
662 |
cur_ticks=GetTickCount(); |
|
663 |
if ((_fast_forward && !_pause) || cur_ticks > next_tick) |
|
664 |
next_tick = cur_ticks; |
|
665 |
||
666 |
if (cur_ticks == next_tick) { |
|
667 |
next_tick += 30; |
|
668 |
_ctrl_pressed = _wnd.has_focus && GetAsyncKeyState(VK_CONTROL)<0; |
|
669 |
_shift_pressed = _wnd.has_focus && GetAsyncKeyState(VK_SHIFT)<0; |
|
670 |
_dbg_screen_rect = _wnd.has_focus && GetAsyncKeyState(VK_CAPITAL)<0; |
|
671 |
||
672 |
// determine which directional keys are down |
|
673 |
_dirkeys = |
|
674 |
(GetAsyncKeyState(VK_LEFT) < 0 ? 1 : 0) + |
|
675 |
(GetAsyncKeyState(VK_UP) < 0 ? 2 : 0) + |
|
676 |
(GetAsyncKeyState(VK_RIGHT) < 0 ? 4 : 0) + |
|
677 |
(GetAsyncKeyState(VK_DOWN) < 0 ? 8 : 0); |
|
678 |
||
679 |
GameLoop(); |
|
680 |
_cursor.delta.x = _cursor.delta.y = 0; |
|
681 |
||
682 |
if (_force_full_redraw) |
|
683 |
MarkWholeScreenDirty(); |
|
684 |
||
685 |
GdiFlush(); |
|
686 |
_screen.dst_ptr = _wnd.buffer_bits; |
|
687 |
UpdateWindows(); |
|
688 |
CheckPaletteAnim(); |
|
689 |
} else { |
|
690 |
Sleep(1); |
|
691 |
GdiFlush(); |
|
692 |
_screen.dst_ptr = _wnd.buffer_bits; |
|
693 |
DrawMouseCursor(); |
|
694 |
} |
|
695 |
} |
|
696 |
} |
|
697 |
||
698 |
static bool Win32GdiChangeRes(int w, int h) |
|
699 |
{ |
|
700 |
_wnd.width = _wnd.width_org = w; |
|
701 |
_wnd.height = _wnd.height_org = h; |
|
702 |
||
703 |
MakeWindow(_wnd.fullscreen); |
|
704 |
||
705 |
return true; |
|
706 |
} |
|
707 |
||
708 |
const HalVideoDriver _win32_video_driver = { |
|
709 |
Win32GdiStart, |
|
710 |
Win32GdiStop, |
|
711 |
Win32GdiMakeDirty, |
|
712 |
Win32GdiMainLoop, |
|
713 |
Win32GdiChangeRes, |
|
714 |
}; |
|
715 |
||
716 |
||
717 |
/********************** |
|
718 |
* WIN32 MIDI PLAYER |
|
719 |
**********************/ |
|
720 |
||
721 |
struct { |
|
722 |
bool stop_song; |
|
723 |
bool terminate; |
|
724 |
bool playing; |
|
725 |
int new_vol; |
|
726 |
HANDLE wait_obj; |
|
727 |
char start_song[260]; |
|
728 |
} _midi; |
|
729 |
||
730 |
static void Win32MidiPlaySong(const char *filename) |
|
731 |
{ |
|
732 |
strcpy(_midi.start_song, filename); |
|
733 |
_midi.playing = true; |
|
734 |
_midi.stop_song = false; |
|
735 |
SetEvent(_midi.wait_obj); |
|
736 |
} |
|
737 |
||
738 |
static void Win32MidiStopSong() |
|
739 |
{ |
|
740 |
if (_midi.playing) { |
|
741 |
_midi.stop_song = true; |
|
742 |
_midi.start_song[0] = 0; |
|
743 |
SetEvent(_midi.wait_obj); |
|
744 |
} |
|
745 |
} |
|
746 |
||
747 |
static bool Win32MidiIsSongPlaying() |
|
748 |
{ |
|
749 |
return _midi.playing; |
|
750 |
} |
|
751 |
||
752 |
static void Win32MidiSetVolume(byte vol) |
|
753 |
{ |
|
754 |
_midi.new_vol = vol; |
|
755 |
SetEvent(_midi.wait_obj); |
|
756 |
} |
|
757 |
||
758 |
static long CDECL MidiSendCommand(const char *cmd, ...) { |
|
759 |
va_list va; |
|
760 |
char buf[512]; |
|
761 |
va_start(va, cmd); |
|
762 |
vsprintf(buf, cmd, va); |
|
763 |
va_end(va); |
|
764 |
return mciSendStringA(buf, NULL, 0, 0); |
|
765 |
} |
|
766 |
||
767 |
static bool MidiIntPlaySong(const char *filename) |
|
768 |
{ |
|
769 |
MidiSendCommand("close all"); |
|
770 |
if (MidiSendCommand("open %s type sequencer alias song", filename) != 0) |
|
771 |
return false; |
|
772 |
||
773 |
if (MidiSendCommand("play song from 0") != 0) |
|
774 |
return false; |
|
775 |
return true; |
|
776 |
} |
|
777 |
||
778 |
static void MidiIntStopSong() |
|
779 |
{ |
|
780 |
MidiSendCommand("close all"); |
|
781 |
} |
|
782 |
||
783 |
static void MidiIntSetVolume(int vol) |
|
784 |
{ |
|
785 |
uint v = (vol * 65535 / 127); |
|
786 |
midiOutSetVolume((HMIDIOUT)-1, v + (v<<16)); |
|
787 |
} |
|
788 |
||
789 |
static bool MidiIntIsSongPlaying() |
|
790 |
{ |
|
791 |
char buf[16]; |
|
792 |
mciSendStringA("status song mode", buf, sizeof(buf), 0); |
|
793 |
return strcmp(buf, "playing") == 0 || strcmp(buf, "seeking") == 0; |
|
794 |
} |
|
795 |
||
796 |
static DWORD WINAPI MidiThread(LPVOID arg) |
|
797 |
{ |
|
798 |
char *s; |
|
799 |
int vol; |
|
800 |
||
801 |
_midi.wait_obj = CreateEvent(NULL, FALSE, FALSE, NULL); |
|
802 |
||
803 |
do { |
|
804 |
if ((vol=_midi.new_vol) != -1) { |
|
805 |
_midi.new_vol = -1; |
|
806 |
MidiIntSetVolume(vol); |
|
807 |
||
808 |
} |
|
809 |
if ((s=_midi.start_song)[0]) { |
|
810 |
_midi.playing = MidiIntPlaySong(s); |
|
811 |
s[0] = 0; |
|
812 |
||
813 |
// Delay somewhat in case we don't manage to play. |
|
814 |
if (!_midi.playing) { |
|
815 |
Sleep(5000); |
|
816 |
} |
|
817 |
} |
|
818 |
if (_midi.stop_song != false && _midi.playing) { |
|
819 |
_midi.stop_song = false; |
|
820 |
_midi.playing = false; |
|
821 |
MidiIntStopSong(); |
|
822 |
} |
|
823 |
||
824 |
if (_midi.playing && !MidiIntIsSongPlaying()) |
|
825 |
_midi.playing = false; |
|
826 |
||
827 |
WaitForMultipleObjects(1, &_midi.wait_obj, FALSE, 1000); |
|
828 |
} while (!_midi.terminate); |
|
829 |
||
830 |
DeleteObject(_midi.wait_obj); |
|
831 |
return 0; |
|
832 |
} |
|
833 |
||
834 |
static char *Win32MidiStart(char **parm) |
|
835 |
{ |
|
836 |
DWORD threadId; |
|
837 |
memset(&_midi, 0, sizeof(_midi)); |
|
838 |
_midi.new_vol = -1; |
|
839 |
CreateThread(NULL, 8192, MidiThread, 0, 0, &threadId); |
|
840 |
return 0; |
|
841 |
} |
|
842 |
||
843 |
static void Win32MidiStop() |
|
844 |
{ |
|
845 |
_midi.terminate = true; |
|
846 |
SetEvent(_midi.wait_obj); |
|
847 |
} |
|
848 |
||
849 |
const HalMusicDriver _win32_music_driver = { |
|
850 |
Win32MidiStart, |
|
851 |
Win32MidiStop, |
|
852 |
Win32MidiPlaySong, |
|
853 |
Win32MidiStopSong, |
|
854 |
Win32MidiIsSongPlaying, |
|
855 |
Win32MidiSetVolume, |
|
856 |
}; |
|
857 |
||
858 |
// WIN32 Sound code. |
|
859 |
||
860 |
static HWAVEOUT _waveout; |
|
861 |
static WAVEHDR _wave_hdr[2]; |
|
862 |
static int _bufsize; |
|
863 |
static void PrepareHeader(WAVEHDR *hdr) |
|
864 |
{ |
|
865 |
hdr->dwBufferLength = _bufsize*4; |
|
866 |
hdr->dwFlags = 0; |
|
867 |
hdr->lpData = malloc(_bufsize*4); |
|
868 |
if (hdr->lpData == NULL || waveOutPrepareHeader(_waveout, hdr, sizeof(WAVEHDR)) != MMSYSERR_NOERROR) |
|
869 |
error("waveOutPrepareHeader failed"); |
|
870 |
} |
|
871 |
||
872 |
static void FillHeaders() |
|
873 |
{ |
|
874 |
WAVEHDR *hdr; |
|
875 |
for(hdr=_wave_hdr; hdr != endof(_wave_hdr); hdr++) { |
|
876 |
if (!(hdr->dwFlags & WHDR_INQUEUE)) { |
|
877 |
MxMixSamples(_mixer, hdr->lpData, hdr->dwBufferLength >> 2); |
|
878 |
if (waveOutWrite(_waveout, hdr, sizeof(WAVEHDR)) != MMSYSERR_NOERROR) |
|
879 |
error("waveOutWrite failed"); |
|
880 |
} |
|
881 |
} |
|
882 |
} |
|
883 |
||
884 |
static void CALLBACK waveOutProc(HWAVEOUT hwo, UINT uMsg, DWORD dwInstance, DWORD dwParam1, DWORD dwParam2) |
|
885 |
{ |
|
886 |
switch(uMsg) { |
|
887 |
case WOM_DONE: |
|
888 |
if (_waveout) |
|
889 |
FillHeaders(); |
|
890 |
break; |
|
891 |
} |
|
892 |
} |
|
893 |
||
894 |
static char *Win32SoundStart(char **parm) |
|
895 |
{ |
|
896 |
WAVEFORMATEX wfex; |
|
897 |
int hz; |
|
898 |
||
899 |
_bufsize = GetDriverParamInt(parm, "bufsize", 1024); |
|
900 |
hz = GetDriverParamInt(parm, "hz", 11025); |
|
901 |
wfex.wFormatTag = WAVE_FORMAT_PCM; |
|
902 |
wfex.nChannels = 2; |
|
903 |
wfex.nSamplesPerSec = hz; |
|
904 |
wfex.nAvgBytesPerSec = hz*2*2; |
|
905 |
wfex.nBlockAlign = 4; |
|
906 |
wfex.wBitsPerSample = 16; |
|
907 |
if (waveOutOpen(&_waveout, WAVE_MAPPER, &wfex, (DWORD)&waveOutProc, 0, CALLBACK_FUNCTION) != MMSYSERR_NOERROR) |
|
908 |
return "waveOutOpen failed"; |
|
909 |
PrepareHeader(&_wave_hdr[0]); |
|
910 |
PrepareHeader(&_wave_hdr[1]); |
|
911 |
FillHeaders(); |
|
912 |
return NULL; |
|
913 |
} |
|
914 |
||
915 |
static void Win32SoundStop() |
|
916 |
{ |
|
917 |
HWAVEOUT waveout = _waveout; |
|
918 |
_waveout = NULL; |
|
919 |
waveOutReset(waveout); |
|
920 |
waveOutUnprepareHeader(waveout, &_wave_hdr[0], sizeof(WAVEHDR)); |
|
921 |
waveOutUnprepareHeader(waveout, &_wave_hdr[1], sizeof(WAVEHDR)); |
|
922 |
waveOutClose(waveout); |
|
923 |
} |
|
924 |
||
925 |
const HalSoundDriver _win32_sound_driver = { |
|
926 |
Win32SoundStart, |
|
927 |
Win32SoundStop, |
|
928 |
}; |
|
929 |
||
930 |
// Helper function needed by dynamically loading SDL |
|
931 |
bool LoadLibraryList(void **proc, const char *dll) |
|
932 |
{ |
|
933 |
HMODULE lib; |
|
934 |
void *p; |
|
935 |
||
936 |
while (*dll) { |
|
937 |
lib = LoadLibrary(dll); |
|
938 |
if (lib == NULL) |
|
939 |
return false; |
|
940 |
while (true) { |
|
941 |
while(*dll++); |
|
942 |
if (!*dll) |
|
943 |
break; |
|
944 |
p = GetProcAddress(lib, dll); |
|
945 |
if (p == NULL) |
|
946 |
return false; |
|
947 |
*proc++ = p; |
|
948 |
} |
|
949 |
dll++; |
|
950 |
} |
|
951 |
return true; |
|
952 |
} |
|
953 |
||
954 |
static const char *_exception_string; |
|
955 |
static void *_safe_esp; |
|
956 |
static char *_crash_msg; |
|
957 |
static bool _expanded; |
|
958 |
static bool _did_emerg_save; |
|
959 |
static int _ident; |
|
960 |
||
961 |
void ShowOSErrorBox(const char *buf) |
|
962 |
{ |
|
963 |
MyShowCursor(true); |
|
964 |
MessageBoxA(GetActiveWindow(), buf, "Error!", MB_ICONSTOP); |
|
965 |
||
966 |
// if exception tracker is enabled, we crash here to let the exception handler handle it. |
|
967 |
#if defined(WIN32_EXCEPTION_TRACKER) && !defined(_DEBUG) |
|
968 |
if (*buf == '!') { |
|
969 |
_exception_string = buf; |
|
970 |
*(byte*)0 = 0; |
|
971 |
} |
|
972 |
#endif |
|
973 |
||
974 |
} |
|
975 |
||
976 |
#ifdef _MSC_VER |
|
977 |
||
978 |
typedef struct DebugFileInfo { |
|
979 |
uint32 size; |
|
980 |
uint32 crc32; |
|
981 |
SYSTEMTIME file_time; |
|
982 |
} DebugFileInfo; |
|
983 |
||
984 |
||
985 |
||
986 |
static uint32 *_crc_table; |
|
987 |
||
988 |
static void MakeCRCTable(uint32 *table) { |
|
989 |
uint32 crc, poly = 0xEDB88320L; |
|
990 |
int i, j; |
|
991 |
||
992 |
_crc_table = table; |
|
993 |
||
994 |
for (i=0; i!=256; i++) { |
|
995 |
crc = i; |
|
996 |
for (j=8; j!=0; j--) { |
|
997 |
if (crc & 1) |
|
998 |
crc = (crc >> 1) ^ poly; |
|
999 |
else |
|
1000 |
crc>>=1; |
|
1001 |
} |
|
1002 |
table[i] = crc; |
|
1003 |
} |
|
1004 |
} |
|
1005 |
||
1006 |
static uint32 CalcCRC(byte *data, uint size, uint32 crc) { |
|
1007 |
do { |
|
1008 |
crc = ((crc>>8) & 0x00FFFFFF) ^ _crc_table[ (crc^(*data++)) & 0xFF ]; |
|
1009 |
} while (--size); |
|
1010 |
return crc; |
|
1011 |
} |
|
1012 |
||
1013 |
static void GetFileInfo(DebugFileInfo *dfi, const char *filename) |
|
1014 |
{ |
|
1015 |
memset(dfi, 0, sizeof(dfi)); |
|
1016 |
||
1017 |
{ |
|
1018 |
HANDLE file; |
|
1019 |
byte buffer[1024]; |
|
1020 |
DWORD numread; |
|
1021 |
uint32 filesize = 0; |
|
1022 |
FILETIME write_time; |
|
1023 |
uint32 crc = (uint32)-1; |
|
1024 |
||
1025 |
file = CreateFile(filename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, 0); |
|
1026 |
if (file != INVALID_HANDLE_VALUE) { |
|
1027 |
while(true) { |
|
1028 |
if (ReadFile(file, buffer, sizeof(buffer), &numread, NULL) == 0 || numread==0) |
|
1029 |
break; |
|
1030 |
filesize += numread; |
|
1031 |
crc = CalcCRC(buffer, numread, crc); |
|
1032 |
} |
|
1033 |
dfi->size = filesize; |
|
1034 |
dfi->crc32 = crc ^ (uint32)-1; |
|
1035 |
||
1036 |
if (GetFileTime(file, NULL, NULL, &write_time)) { |
|
1037 |
FileTimeToSystemTime(&write_time, &dfi->file_time); |
|
1038 |
} |
|
1039 |
CloseHandle(file); |
|
1040 |
} |
|
1041 |
} |
|
1042 |
} |
|
1043 |
||
1044 |
||
1045 |
static char *PrintModuleInfo(char *output, HMODULE mod) |
|
1046 |
{ |
|
1047 |
char *buffer = alloca(MAX_PATH); |
|
1048 |
DebugFileInfo dfi; |
|
1049 |
GetModuleFileName(mod, buffer, MAX_PATH); |
|
1050 |
GetFileInfo(&dfi, buffer); |
|
1051 |
output += sprintf(output, " %-20s handle: %.8X size: %d crc: %.8X date: %d-%.2d-%.2d %.2d:%.2d:%.2d\r\n", |
|
1052 |
buffer, |
|
1053 |
mod, |
|
1054 |
dfi.size, |
|
1055 |
dfi.crc32, |
|
1056 |
dfi.file_time.wYear, |
|
1057 |
dfi.file_time.wMonth, |
|
1058 |
dfi.file_time.wDay, |
|
1059 |
dfi.file_time.wHour, |
|
1060 |
dfi.file_time.wMinute, |
|
1061 |
dfi.file_time.wSecond |
|
1062 |
); |
|
1063 |
return output; |
|
1064 |
} |
|
1065 |
||
1066 |
static char *PrintModuleList(char *output) |
|
1067 |
{ |
|
1068 |
BOOL (WINAPI *EnumProcessModules)(HANDLE,HMODULE*,DWORD,LPDWORD); |
|
1069 |
HANDLE proc; |
|
1070 |
HMODULE modules[100]; |
|
1071 |
DWORD needed; |
|
1072 |
BOOL res; |
|
1073 |
int count,i; |
|
1074 |
||
1075 |
if (LoadLibraryList((void*)&EnumProcessModules, "psapi.dll\0EnumProcessModules\0")) { |
|
1076 |
proc = OpenProcess(PROCESS_ALL_ACCESS, FALSE, GetCurrentProcessId()); |
|
1077 |
if (proc) { |
|
1078 |
res = EnumProcessModules(proc, modules, sizeof(modules), &needed); |
|
1079 |
CloseHandle(proc); |
|
1080 |
if (res) { |
|
1081 |
count = min(needed/sizeof(HMODULE*), sizeof(modules)/sizeof(HMODULE*)); |
|
1082 |
for(i=0; i!=count; i++) |
|
1083 |
output = PrintModuleInfo(output, modules[i]); |
|
1084 |
return output; |
|
1085 |
} |
|
1086 |
} |
|
1087 |
} |
|
1088 |
output = PrintModuleInfo(output, NULL); |
|
1089 |
return output; |
|
1090 |
} |
|
1091 |
||
1092 |
static const char _crash_desc[] = |
|
1093 |
"A serious fault condition occured in the game. The game will shut down.\n" |
|
1094 |
"Press \"Submit report\" to send crash information to the developers. " |
|
1095 |
"This will greatly help debugging. The information contained in the report is " |
|
1096 |
"displayed below.\n" |
|
1097 |
"Press \"Emergency save\" to attempt saving the game."; |
|
1098 |
||
1099 |
static const char _save_succeeded[] = |
|
1100 |
"Emergency save succeeded.\nBe aware that critical parts of the internal game state " |
|
1101 |
"may have become corrupted. The saved game is not guaranteed to work."; |
|
1102 |
||
1103 |
bool EmergencySave(); |
|
1104 |
||
1105 |
||
1106 |
typedef struct { |
|
1107 |
HINTERNET (WINAPI *InternetOpenA)(LPCSTR,DWORD, LPCSTR, LPCSTR, DWORD); |
|
1108 |
HINTERNET (WINAPI *InternetConnectA)(HINTERNET, LPCSTR, INTERNET_PORT, LPCSTR, LPCSTR, DWORD, DWORD, DWORD); |
|
1109 |
HINTERNET (WINAPI *HttpOpenRequestA)(HINTERNET, LPCSTR, LPCSTR, LPCSTR, LPCSTR, LPCSTR *, DWORD, DWORD); |
|
1110 |
BOOL (WINAPI *HttpSendRequestA)(HINTERNET, LPCSTR, DWORD, LPVOID, DWORD); |
|
1111 |
BOOL (WINAPI *InternetCloseHandle)(HINTERNET); |
|
1112 |
BOOL (WINAPI *HttpQueryInfo)(HINTERNET, DWORD, LPVOID, LPDWORD, LPDWORD); |
|
1113 |
} WinInetProcs; |
|
1114 |
||
1115 |
#define M(x) x "\0" |
|
1116 |
static const char wininet_files[] = |
|
1117 |
M("wininet.dll") |
|
1118 |
M("InternetOpenA") |
|
1119 |
M("InternetConnectA") |
|
1120 |
M("HttpOpenRequestA") |
|
1121 |
M("HttpSendRequestA") |
|
1122 |
M("InternetCloseHandle") |
|
1123 |
M("HttpQueryInfoA") |
|
1124 |
M(""); |
|
1125 |
#undef M |
|
1126 |
||
1127 |
static WinInetProcs _wininet; |
|
1128 |
||
1129 |
||
1130 |
static char *SubmitCrashReport(HWND wnd, void *msg, size_t msglen, const char *arg) |
|
1131 |
{ |
|
1132 |
HINTERNET inet, conn, http; |
|
1133 |
char *err = NULL; |
|
1134 |
DWORD code, len; |
|
1135 |
static char buf[100]; |
|
1136 |
char buff[100]; |
|
1137 |
||
1138 |
if (_wininet.InternetOpen == NULL && !LoadLibraryList((void**)&_wininet, wininet_files)) return "can't load wininet.dll"; |
|
1139 |
||
1140 |
inet = _wininet.InternetOpen("TTD", INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0 ); |
|
1141 |
if (inet == NULL) { err = "internetopen failed"; goto error1; } |
|
1142 |
||
1143 |
conn = _wininet.InternetConnect(inet, "openttd.com", INTERNET_DEFAULT_HTTP_PORT, "", "", INTERNET_SERVICE_HTTP, 0, 0); |
|
1144 |
if (conn == NULL) { err = "internetconnect failed"; goto error2; } |
|
1145 |
||
1146 |
sprintf(buff, "/crash.php?file=%s&ident=%d", arg, _ident); |
|
1147 |
||
1148 |
http = _wininet.HttpOpenRequest(conn, "POST", buff, NULL, NULL, NULL, INTERNET_FLAG_NO_CACHE_WRITE , 0); |
|
1149 |
if (http == NULL) { err = "httpopenrequest failed"; goto error3; } |
|
1150 |
||
1151 |
if (!_wininet.HttpSendRequest(http, "Content-type: application/binary", -1, msg, msglen)) { err = "httpsendrequest failed"; goto error4; } |
|
1152 |
||
1153 |
len = sizeof(code); |
|
1154 |
if (!_wininet.HttpQueryInfo(http, HTTP_QUERY_STATUS_CODE | HTTP_QUERY_FLAG_NUMBER, &code, &len, 0)) { err = "httpqueryinfo failed"; goto error4; } |
|
1155 |
||
1156 |
if (code != 200) { |
|
1157 |
int l = sprintf(buf, "Server said: %d ", code); |
|
1158 |
len = sizeof(buf) - l; |
|
1159 |
_wininet.HttpQueryInfo(http, HTTP_QUERY_STATUS_TEXT, buf + l, &len, 0); |
|
1160 |
err = buf; |
|
1161 |
} |
|
1162 |
||
1163 |
error4: |
|
1164 |
_wininet.InternetCloseHandle(http); |
|
1165 |
error3: |
|
1166 |
_wininet.InternetCloseHandle(conn); |
|
1167 |
error2: |
|
1168 |
_wininet.InternetCloseHandle(inet); |
|
1169 |
error1: |
|
1170 |
return err; |
|
1171 |
} |
|
1172 |
||
1173 |
static void SubmitFile(HWND wnd, const char *file) |
|
1174 |
{ |
|
1175 |
HANDLE h; |
|
1176 |
unsigned long size; |
|
1177 |
unsigned long read; |
|
1178 |
void *mem; |
|
1179 |
||
1180 |
h = CreateFile(file, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); |
|
1181 |
if (h == NULL) return; |
|
1182 |
||
1183 |
size = GetFileSize(h, NULL); |
|
1184 |
if (size > 500000) goto error1; |
|
1185 |
||
1186 |
mem = malloc(size); |
|
1187 |
if (mem == NULL) goto error1; |
|
1188 |
||
1189 |
if (!ReadFile(h, mem, size, &read, NULL) || read != size) goto error2; |
|
1190 |
||
1191 |
SubmitCrashReport(wnd, mem, size, file); |
|
1192 |
||
1193 |
error2: |
|
1194 |
free(mem); |
|
1195 |
error1: |
|
1196 |
CloseHandle(h); |
|
1197 |
} |
|
1198 |
||
1199 |
static const char * const _expand_texts[] = {"S&how report >>", "&Hide report <<" }; |
|
1200 |
||
1201 |
static void SetWndSize(HWND wnd, int mode) |
|
1202 |
{ |
|
1203 |
RECT r,r2; |
|
1204 |
int offs; |
|
1205 |
||
1206 |
GetWindowRect(wnd, &r); |
|
1207 |
||
1208 |
SetDlgItemText(wnd, 15, _expand_texts[mode == 1]); |
|
1209 |
||
1210 |
if (mode >= 0) { |
|
1211 |
GetWindowRect(GetDlgItem(wnd, 11), &r2); |
|
1212 |
offs = r2.bottom - r2.top + 10; |
|
1213 |
if (!mode) offs=-offs; |
|
1214 |
SetWindowPos(wnd, HWND_TOPMOST, 0, 0, r.right - r.left, r.bottom - r.top + offs, SWP_NOMOVE | SWP_NOZORDER); |
|
1215 |
} else { |
|
1216 |
SetWindowPos(wnd, HWND_TOPMOST, |
|
1217 |
(GetSystemMetrics(SM_CXSCREEN) - (r.right - r.left)) >> 1, |
|
1218 |
(GetSystemMetrics(SM_CYSCREEN) - (r.bottom - r.top)) >> 1, |
|
1219 |
0, 0, SWP_NOSIZE); |
|
1220 |
} |
|
1221 |
} |
|
1222 |
||
1223 |
static bool DoEmergencySave(HWND wnd) |
|
1224 |
{ |
|
1225 |
bool b = false; |
|
1226 |
||
1227 |
EnableWindow(GetDlgItem(wnd, 13), FALSE); |
|
1228 |
_did_emerg_save = true; |
|
1229 |
__try { |
|
1230 |
b = EmergencySave(); |
|
1231 |
} __except (1) {} |
|
1232 |
return b; |
|
1233 |
} |
|
1234 |
||
1235 |
static BOOL CALLBACK CrashDialogFunc(HWND wnd,UINT msg,WPARAM wParam,LPARAM lParam) |
|
1236 |
{ |
|
1237 |
switch(msg) { |
|
1238 |
case WM_INITDIALOG: |
|
1239 |
SetDlgItemText(wnd, 10, _crash_desc); |
|
1240 |
SetDlgItemText(wnd, 11, _crash_msg); |
|
1241 |
SendDlgItemMessage(wnd, 11, WM_SETFONT, (WPARAM)GetStockObject(ANSI_FIXED_FONT), FALSE); |
|
1242 |
SetWndSize(wnd, -1); |
|
1243 |
return TRUE; |
|
1244 |
case WM_COMMAND: |
|
1245 |
switch(wParam) { |
|
1246 |
case 12: // Close |
|
1247 |
ExitProcess(0); |
|
1248 |
case 13: { // Emergency save |
|
1249 |
if (DoEmergencySave(wnd)) |
|
1250 |
MessageBoxA(wnd, _save_succeeded, "Save successful", MB_ICONINFORMATION); |
|
1251 |
else |
|
1252 |
MessageBoxA(wnd, "Save failed", "Save failed", MB_ICONINFORMATION); |
|
1253 |
break; |
|
1254 |
} |
|
1255 |
case 14: { // Submit crash report |
|
1256 |
char *s; |
|
1257 |
||
1258 |
SetCursor(LoadCursor(NULL, IDC_WAIT)); |
|
1259 |
||
1260 |
s = SubmitCrashReport(wnd, _crash_msg, strlen(_crash_msg), ""); |
|
1261 |
if (s) { |
|
1262 |
MessageBoxA(wnd, s, "Error", MB_ICONSTOP); |
|
1263 |
break; |
|
1264 |
} |
|
1265 |
||
1266 |
// try to submit emergency savegame |
|
1267 |
if (_did_emerg_save || DoEmergencySave(wnd)) { |
|
1268 |
SubmitFile(wnd, "crash.sav"); |
|
1269 |
} |
|
1270 |
// try to submit the autosaved game |
|
1271 |
if (_opt.autosave) { |
|
1272 |
char buf[40]; |
|
1273 |
sprintf(buf, "autosave%d.sav", (_autosave_ctr - 1) & 3); |
|
1274 |
SubmitFile(wnd, buf); |
|
1275 |
} |
|
1276 |
EnableWindow(GetDlgItem(wnd, 14), FALSE); |
|
1277 |
SetCursor(LoadCursor(NULL, IDC_ARROW)); |
|
1278 |
MessageBoxA(wnd, "Crash report submitted. Thank you.", "Crash Report", MB_ICONINFORMATION); |
|
1279 |
break; |
|
1280 |
} |
|
1281 |
case 15: // Expand |
|
1282 |
_expanded ^= 1; |
|
1283 |
SetWndSize(wnd, _expanded); |
|
1284 |
break; |
|
1285 |
} |
|
1286 |
return TRUE; |
|
1287 |
case WM_CLOSE: |
|
1288 |
ExitProcess(0); |
|
1289 |
} |
|
1290 |
||
1291 |
return FALSE; |
|
1292 |
} |
|
1293 |
||
1294 |
static void Handler2() |
|
1295 |
{ |
|
1296 |
ShowCursor(TRUE); |
|
1297 |
ShowWindow(GetActiveWindow(), FALSE); |
|
1298 |
DialogBox(GetModuleHandle(NULL), MAKEINTRESOURCE(100), NULL, CrashDialogFunc); |
|
1299 |
} |
|
1300 |
||
1301 |
static LONG WINAPI ExceptionHandler(EXCEPTION_POINTERS *ep) |
|
1302 |
{ |
|
1303 |
char *output; |
|
1304 |
static bool had_exception; |
|
1305 |
||
1306 |
if (had_exception) { ExitProcess(0); } |
|
1307 |
had_exception = true; |
|
1308 |
||
1309 |
_ident = GetTickCount(); // something pretty unique |
|
1310 |
||
1311 |
MakeCRCTable(alloca(256 * sizeof(uint32))); |
|
1312 |
_crash_msg = output = LocalAlloc(LMEM_FIXED, 8192); |
|
1313 |
||
1314 |
{ |
|
1315 |
SYSTEMTIME time; |
|
1316 |
GetLocalTime(&time); |
|
1317 |
output += sprintf(output, |
|
1318 |
"*** OpenTTD Crash Report ***\r\n" |
|
1319 |
"Date: %d-%.2d-%.2d %.2d:%.2d:%.2d\r\n" |
|
1320 |
"Build: %s built on " __TIMESTAMP__ "\r\n", |
|
1321 |
time.wYear, |
|
1322 |
time.wMonth, |
|
1323 |
time.wDay, |
|
1324 |
time.wHour, |
|
1325 |
time.wMinute, |
|
1326 |
time.wSecond, |
|
1327 |
"???" |
|
1328 |
); |
|
1329 |
} |
|
1330 |
||
1331 |
if (_exception_string) output += sprintf(output, "Reason: %s\r\n", _exception_string); |
|
1332 |
||
1333 |
output += sprintf(output, "Exception %.8X at %.8X\r\n" |
|
1334 |
"Registers:\r\n" |
|
1335 |
" EAX: %.8X EBX: %.8X ECX: %.8X EDX: %.8X\r\n" |
|
1336 |
" ESI: %.8X EDI: %.8X EBP: %.8X ESP: %.8X\r\n" |
|
1337 |
" EIP: %.8X EFLAGS: %.8X\r\n" |
|
1338 |
"\r\nBytes at CS:EIP:\r\n", |
|
1339 |
ep->ExceptionRecord->ExceptionCode, |
|
1340 |
ep->ExceptionRecord->ExceptionAddress, |
|
1341 |
ep->ContextRecord->Eax, |
|
1342 |
ep->ContextRecord->Ebx, |
|
1343 |
ep->ContextRecord->Ecx, |
|
1344 |
ep->ContextRecord->Edx, |
|
1345 |
ep->ContextRecord->Esi, |
|
1346 |
ep->ContextRecord->Edi, |
|
1347 |
ep->ContextRecord->Ebp, |
|
1348 |
ep->ContextRecord->Esp, |
|
1349 |
ep->ContextRecord->Eip, |
|
1350 |
ep->ContextRecord->EFlags |
|
1351 |
); |
|
1352 |
||
1353 |
{ |
|
1354 |
byte *b = (byte*)ep->ContextRecord->Eip; |
|
1355 |
int i; |
|
1356 |
for(i=0; i!=24; i++) { |
|
1357 |
if (IsBadReadPtr(b, 1)) { |
|
1358 |
output += sprintf(output, " ??"); // OCR: WAS: , 0); |
|
1359 |
} else { |
|
1360 |
output += sprintf(output, " %.2X", *b); |
|
1361 |
} |
|
1362 |
b++; |
|
1363 |
} |
|
1364 |
output += sprintf(output, |
|
1365 |
"\r\n" |
|
1366 |
"\r\nStack trace: \r\n" |
|
1367 |
); |
|
1368 |
} |
|
1369 |
||
1370 |
{ |
|
1371 |
int i,j; |
|
1372 |
uint32 *b = (uint32*)ep->ContextRecord->Esp; |
|
1373 |
for(j=0; j!=24; j++) { |
|
1374 |
for(i=0; i!=8; i++) { |
|
1375 |
if (IsBadReadPtr(b,sizeof(uint32))) { |
|
1376 |
output += sprintf(output, " ????????"); //OCR: WAS - , 0); |
|
1377 |
} else { |
|
1378 |
output += sprintf(output, " %.8X", *b); |
|
1379 |
} |
|
1380 |
b++; |
|
1381 |
} |
|
1382 |
output += sprintf(output, "\r\n"); |
|
1383 |
} |
|
1384 |
} |
|
1385 |
||
1386 |
output += sprintf(output, "\r\nModule information:\r\n"); |
|
1387 |
output = PrintModuleList(output); |
|
1388 |
||
1389 |
{ |
|
1390 |
OSVERSIONINFO os; |
|
1391 |
os.dwOSVersionInfoSize = sizeof(os); |
|
1392 |
GetVersionEx(&os); |
|
1393 |
output += sprintf(output, "\r\nSystem information:\r\n" |
|
1394 |
" Windows version %d.%d %d %s\r\n", os.dwMajorVersion, os.dwMinorVersion, os.dwBuildNumber, os.szCSDVersion); |
|
1395 |
} |
|
1396 |
||
1397 |
{ |
|
1398 |
HANDLE file = CreateFile("crash.log", GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, 0); |
|
1399 |
DWORD num_written; |
|
1400 |
if (file != INVALID_HANDLE_VALUE) { |
|
1401 |
WriteFile(file, _crash_msg, output - _crash_msg, &num_written, NULL); |
|
1402 |
CloseHandle(file); |
|
1403 |
} |
|
1404 |
} |
|
1405 |
||
1406 |
if (_safe_esp) { |
|
1407 |
ep->ContextRecord->Eip = (DWORD)Handler2; |
|
1408 |
ep->ContextRecord->Esp = (DWORD)_safe_esp; |
|
1409 |
return EXCEPTION_CONTINUE_EXECUTION; |
|
1410 |
} else { |
|
1411 |
return EXCEPTION_EXECUTE_HANDLER; |
|
1412 |
} |
|
1413 |
} |
|
1414 |
||
1415 |
static void Win32InitializeExceptions() |
|
1416 |
{ |
|
1417 |
_asm { |
|
1418 |
mov _safe_esp,esp |
|
1419 |
} |
|
1420 |
||
1421 |
SetUnhandledExceptionFilter(ExceptionHandler); |
|
1422 |
} |
|
1423 |
#endif |
|
1424 |
||
1425 |
static char *_fios_path; |
|
1426 |
static char *_fios_save_path; |
|
1427 |
static char *_fios_scn_path; |
|
1428 |
static FiosItem *_fios_items; |
|
1429 |
static int _fios_count, _fios_alloc; |
|
1430 |
||
1431 |
static FiosItem *FiosAlloc() |
|
1432 |
{ |
|
1433 |
if (_fios_count == _fios_alloc) { |
|
1434 |
_fios_alloc += 256; |
|
1435 |
_fios_items = realloc(_fios_items, _fios_alloc * sizeof(FiosItem)); |
|
1436 |
} |
|
1437 |
return &_fios_items[_fios_count++]; |
|
1438 |
} |
|
1439 |
||
1440 |
static HANDLE MyFindFirstFile(char *path, char *file, WIN32_FIND_DATA *fd) |
|
1441 |
{ |
|
1442 |
char paths[MAX_PATH]; |
|
1443 |
||
1444 |
sprintf(paths, "%s\\%s", path, file); |
|
1445 |
return FindFirstFile(paths, fd); |
|
1446 |
} |
|
1447 |
||
1448 |
int CDECL compare_FiosItems (const void *a, const void *b) { |
|
1449 |
const FiosItem *da = (const FiosItem *) a; |
|
1450 |
const FiosItem *db = (const FiosItem *) b; |
|
1451 |
int r; |
|
1452 |
||
1453 |
if (_savegame_sort_order < 2) // sort by date |
|
1454 |
r = da->mtime < db->mtime ? 1 : -1; |
|
1455 |
else |
|
1456 |
r = stricmp(da->title[0] ? da->title : da->name, db->title[0] ? db->title : db->name); |
|
1457 |
||
1458 |
if (_savegame_sort_order & 1) r = -r; |
|
1459 |
return r; |
|
1460 |
} |
|
1461 |
||
1462 |
// Get a list of savegames |
|
1463 |
FiosItem *FiosGetSavegameList(int *num, int mode) |
|
1464 |
{ |
|
1465 |
WIN32_FIND_DATA fd; |
|
1466 |
HANDLE h; |
|
1467 |
FiosItem *fios; |
|
1468 |
int sort_start; |
|
1469 |
char buf[MAX_PATH]; |
|
1470 |
||
1471 |
if (_fios_save_path == NULL) { |
|
1472 |
_fios_save_path = malloc(MAX_PATH); |
|
1473 |
strcpy(_fios_save_path, _path.save_dir); |
|
1474 |
} |
|
1475 |
||
1476 |
if (_game_mode == GM_EDITOR) |
|
1477 |
_fios_path = _fios_scn_path; |
|
1478 |
else |
|
1479 |
_fios_path = _fios_save_path; |
|
1480 |
||
1481 |
// Parent directory, only if not of the type C:\. |
|
1482 |
if (_fios_path[3] != 0) { |
|
1483 |
fios = FiosAlloc(); |
|
1484 |
fios->type = FIOS_TYPE_PARENT; |
|
1485 |
strcpy(fios->title, ".. (Parent directory)"); |
|
1486 |
} |
|
1487 |
||
1488 |
||
1489 |
// Show subdirectories first |
|
1490 |
h = MyFindFirstFile(_fios_path, "*.*", &fd); |
|
1491 |
if (h != INVALID_HANDLE_VALUE) { |
|
1492 |
do { |
|
1493 |
if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY && |
|
1494 |
!(fd.cFileName[0]=='.' && (fd.cFileName[1]==0 || fd.cFileName[1]=='.' && fd.cFileName[2]==0))) { |
|
1495 |
fios = FiosAlloc(); |
|
1496 |
fios->type = FIOS_TYPE_DIR; |
|
1497 |
strcpy(fios->name, fd.cFileName); |
|
1498 |
sprintf(fios->title, "\\%s (Directory)", fd.cFileName); |
|
1499 |
} |
|
1500 |
} while (FindNextFile(h, &fd)); |
|
1501 |
FindClose(h); |
|
1502 |
} |
|
1503 |
||
1504 |
// this is where to start sorting |
|
1505 |
sort_start = _fios_count; |
|
1506 |
||
1507 |
/* Show savegame files |
|
1508 |
* .SAV OpenTTD saved game |
|
1509 |
* .SS1 Transport Tycoon Deluxe preset game |
|
1510 |
* .SV1 Transport Tycoon Deluxe (Patch) saved game |
|
1511 |
* .SV2 Transport Tycoon Deluxe (Patch) saved 2-player game |
|
1512 |
*/ |
|
1513 |
h = MyFindFirstFile(_fios_path, "*.*", &fd); |
|
1514 |
if (h != INVALID_HANDLE_VALUE) { |
|
1515 |
do { |
|
1516 |
if (!(fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) { |
|
1517 |
char *t = strrchr(fd.cFileName, '.'); |
|
1518 |
if (t && !stricmp(t, ".SAV")) { // OpenTTD |
|
1519 |
fios = FiosAlloc(); |
|
1520 |
fios->mtime = *(uint64*)&fd.ftLastWriteTime; |
|
1521 |
sprintf(buf, "%s\\%s", _fios_path, fd.cFileName); |
|
1522 |
fios->type = FIOS_TYPE_FILE; |
|
1523 |
fios->title[0] = 0; |
|
1524 |
ttd_strlcpy(fios->name, fd.cFileName, strlen(fd.cFileName)-3); |
|
1525 |
} else if (mode == SLD_LOAD_GAME || mode == SLD_LOAD_SCENARIO) { |
|
1526 |
int ext = 0; // start of savegame extensions in _old_extensions[] |
|
1527 |
if (t && ((ext++, !stricmp(t, ".SS1")) || (ext++, !stricmp(t, ".SV1")) || (ext++, !stricmp(t, ".SV2"))) ) { // TTDLX(Patch) |
|
1528 |
fios = FiosAlloc(); |
|
1529 |
fios->old_extension = ext-1; |
|
1530 |
fios->mtime = *(uint64*)&fd.ftLastWriteTime; |
|
1531 |
sprintf(buf, "%s\\%s", _fios_path, fd.cFileName); |
|
1532 |
fios->type = FIOS_TYPE_OLDFILE; |
|
1533 |
GetOldSaveGameName(fios->title, buf); |
|
1534 |
ttd_strlcpy(fios->name, fd.cFileName, strlen(fd.cFileName)-3); |
|
1535 |
} |
|
1536 |
} |
|
1537 |
} |
|
1538 |
} while (FindNextFile(h, &fd)); |
|
1539 |
FindClose(h); |
|
1540 |
} |
|
1541 |
||
1542 |
qsort(_fios_items + sort_start, _fios_count - sort_start, sizeof(FiosItem), compare_FiosItems); |
|
1543 |
||
1544 |
// Drives |
|
1545 |
{ |
|
1546 |
char drives[256]; |
|
1547 |
char *s; |
|
1548 |
GetLogicalDriveStrings(sizeof(drives), drives); |
|
1549 |
s=drives; |
|
1550 |
while (*s) { |
|
1551 |
fios = FiosAlloc(); |
|
1552 |
fios->type = FIOS_TYPE_DRIVE; |
|
1553 |
fios->title[0] = s[0]; |
|
1554 |
fios->title[1] = ':'; |
|
1555 |
fios->title[2] = 0; |
|
1556 |
while (*s++) {} |
|
1557 |
} |
|
1558 |
} |
|
1559 |
*num = _fios_count; |
|
1560 |
return _fios_items; |
|
1561 |
} |
|
1562 |
||
1563 |
// Get a list of scenarios |
|
1564 |
FiosItem *FiosGetScenarioList(int *num, int mode) |
|
1565 |
{ |
|
1566 |
FiosItem *fios; |
|
1567 |
WIN32_FIND_DATA fd; |
|
1568 |
HANDLE h; |
|
1569 |
int sort_start; |
|
1570 |
char buf[MAX_PATH]; |
|
1571 |
||
1572 |
if (mode == SLD_NEW_GAME || _fios_scn_path == NULL) { |
|
1573 |
if (_fios_scn_path == NULL) |
|
1574 |
_fios_scn_path = malloc(MAX_PATH); |
|
1575 |
strcpy(_fios_scn_path, _path.scenario_dir); |
|
1576 |
} |
|
1577 |
||
1578 |
_fios_path = _fios_scn_path; |
|
1579 |
||
1580 |
// Parent directory, only if not of the type C:\. |
|
1581 |
if (_fios_path[3] != 0 && mode != SLD_NEW_GAME) { |
|
1582 |
fios = FiosAlloc(); |
|
1583 |
fios->type = FIOS_TYPE_PARENT; |
|
1584 |
strcpy(fios->title, ".. (Parent directory)"); |
|
1585 |
} |
|
1586 |
||
1587 |
// Show subdirectories first |
|
1588 |
h = MyFindFirstFile(_fios_scn_path, "*.*", &fd); |
|
1589 |
if (h != INVALID_HANDLE_VALUE && mode != SLD_NEW_GAME) { |
|
1590 |
do { |
|
1591 |
if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY && |
|
1592 |
!(fd.cFileName[0]=='.' && (fd.cFileName[1]==0 || fd.cFileName[1]=='.' && fd.cFileName[2]==0))) { |
|
1593 |
fios = FiosAlloc(); |
|
1594 |
fios->type = FIOS_TYPE_DIR; |
|
1595 |
strcpy(fios->name, fd.cFileName); |
|
1596 |
sprintf(fios->title, "\\%s (Directory)", fd.cFileName); |
|
1597 |
} |
|
1598 |
} while (FindNextFile(h, &fd)); |
|
1599 |
FindClose(h); |
|
1600 |
} |
|
1601 |
||
1602 |
// this is where to start sorting |
|
1603 |
sort_start = _fios_count; |
|
1604 |
||
1605 |
/* Show scenario files |
|
1606 |
* .SCN OpenTTD style scenario file |
|
1607 |
* .SV0 Transport Tycoon Deluxe (Patch) scenario |
|
1608 |
* .SS0 Transport Tycoon Deluxe preset scenario |
|
1609 |
*/ |
|
1610 |
h = MyFindFirstFile(_fios_scn_path, "*.*", &fd); |
|
1611 |
if (h != INVALID_HANDLE_VALUE) { |
|
1612 |
do { |
|
1613 |
if (!(fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) { |
|
1614 |
char *t = strrchr(fd.cFileName, '.'); |
|
1615 |
if (t && !stricmp(t, ".SCN")) { // OpenTTD |
|
1616 |
fios = FiosAlloc(); |
|
1617 |
fios->mtime = *(uint64*)&fd.ftLastWriteTime; |
|
1618 |
sprintf(buf, "%s\\%s", _fios_path, fd.cFileName); |
|
1619 |
fios->type = FIOS_TYPE_SCENARIO; |
|
1620 |
fios->title[0] = 0; |
|
1621 |
ttd_strlcpy(fios->name, fd.cFileName, strlen(fd.cFileName)-3); |
|
1622 |
} else if (mode == SLD_LOAD_GAME || mode == SLD_LOAD_SCENARIO || mode == SLD_NEW_GAME) { |
|
1623 |
int ext = 3; // start of scenario extensions in _old_extensions[] |
|
1624 |
if (t && ((ext++, !stricmp(t, ".SV0")) || (ext++, !stricmp(t, ".SS0"))) ) { // TTDLX(Patch) |
|
1625 |
fios = FiosAlloc(); |
|
1626 |
fios->old_extension = ext-1; |
|
1627 |
fios->mtime = *(uint64*)&fd.ftLastWriteTime; |
|
1628 |
sprintf(buf, "%s\\%s", _fios_path, fd.cFileName); |
|
1629 |
fios->type = FIOS_TYPE_OLD_SCENARIO; |
|
1630 |
GetOldScenarioGameName(fios->title, buf); |
|
1631 |
ttd_strlcpy(fios->name, fd.cFileName, strlen(fd.cFileName)-3); |
|
1632 |
} |
|
1633 |
} |
|
1634 |
} |
|
1635 |
} while (FindNextFile(h, &fd)); |
|
1636 |
FindClose(h); |
|
1637 |
} |
|
1638 |
||
1639 |
qsort(_fios_items + sort_start, _fios_count - sort_start, sizeof(FiosItem), compare_FiosItems); |
|
1640 |
||
1641 |
// Drives |
|
1642 |
if (mode != SLD_NEW_GAME) { |
|
1643 |
char drives[256]; |
|
1644 |
char *s; |
|
1645 |
GetLogicalDriveStrings(sizeof(drives), drives); |
|
1646 |
s=drives; |
|
1647 |
while (*s) { |
|
1648 |
fios = FiosAlloc(); |
|
1649 |
fios->type = FIOS_TYPE_DRIVE; |
|
1650 |
fios->title[0] = s[0]; |
|
1651 |
fios->title[1] = ':'; |
|
1652 |
fios->title[2] = 0; |
|
1653 |
while (*s++) {} |
|
1654 |
} |
|
1655 |
} |
|
1656 |
||
1657 |
*num = _fios_count; |
|
1658 |
return _fios_items; |
|
1659 |
} |
|
1660 |
||
1661 |
// Free the list of savegames |
|
1662 |
void FiosFreeSavegameList() |
|
1663 |
{ |
|
1664 |
free(_fios_items); |
|
1665 |
_fios_items = NULL; |
|
1666 |
_fios_alloc = _fios_count = 0; |
|
1667 |
} |
|
1668 |
||
1669 |
// Browse to |
|
1670 |
char *FiosBrowseTo(const FiosItem *item) |
|
1671 |
{ |
|
1672 |
char *path = _fios_path; |
|
1673 |
char *s; |
|
1674 |
||
1675 |
switch(item->type) { |
|
1676 |
case FIOS_TYPE_DRIVE: |
|
1677 |
sprintf(path, "%c:\\", item->title[0]); |
|
1678 |
break; |
|
1679 |
||
1680 |
case FIOS_TYPE_PARENT: |
|
1681 |
// Skip drive part |
|
1682 |
path += 3; |
|
1683 |
s = path; |
|
1684 |
while (*path) { |
|
1685 |
if (*path== '\\') |
|
1686 |
s = path; |
|
1687 |
path++; |
|
1688 |
} |
|
1689 |
*s = 0; |
|
1690 |
break; |
|
1691 |
||
1692 |
case FIOS_TYPE_DIR: |
|
1693 |
// Scan to end |
|
1694 |
while (*++path); |
|
1695 |
// Add backslash? |
|
1696 |
if (path[-1] != '\\') *path++ = '\\'; |
|
1697 |
||
1698 |
strcpy(path, item->name); |
|
1699 |
break; |
|
1700 |
||
1701 |
case FIOS_TYPE_FILE: |
|
1702 |
FiosMakeSavegameName(str_buffr, item->name); |
|
1703 |
return str_buffr; |
|
1704 |
||
1705 |
case FIOS_TYPE_OLDFILE: |
|
1706 |
sprintf(str_buffr, "%s\\%s.%s", _fios_path, item->name, _old_extensions[item->old_extension]); |
|
1707 |
return str_buffr; |
|
1708 |
||
1709 |
case FIOS_TYPE_SCENARIO: |
|
1710 |
sprintf(str_buffr, "%s\\%s.scn", path, item->name); |
|
1711 |
return str_buffr; |
|
1712 |
case FIOS_TYPE_OLD_SCENARIO: |
|
1713 |
sprintf(str_buffr, "%s\\%s.%s", path, item->name, _old_extensions[item->old_extension]); |
|
1714 |
return str_buffr; |
|
1715 |
} |
|
1716 |
||
1717 |
return NULL; |
|
1718 |
} |
|
1719 |
||
1720 |
// Get descriptive texts. |
|
1721 |
// Returns a path as well as a |
|
1722 |
// string describing the path. |
|
1723 |
StringID FiosGetDescText(char **path) |
|
1724 |
{ |
|
1725 |
char root[4]; |
|
1726 |
DWORD spc, bps, nfc, tnc; |
|
1727 |
*path = _fios_path; |
|
1728 |
||
1729 |
root[0] = _fios_path[0]; |
|
1730 |
root[1] = ':'; |
|
1731 |
root[2] = '\\'; |
|
1732 |
root[3] = 0; |
|
1733 |
if (GetDiskFreeSpace(root, &spc, &bps, &nfc, &tnc)) { |
|
1734 |
uint32 tot = ((spc*bps)*(uint64)nfc) >> 20; |
|
1735 |
SET_DPARAM32(0, tot); |
|
1736 |
return STR_4005_BYTES_FREE; |
|
1737 |
} else { |
|
1738 |
return STR_4006_UNABLE_TO_READ_DRIVE; |
|
1739 |
} |
|
1740 |
} |
|
1741 |
||
1742 |
void FiosMakeSavegameName(char *buf, const char *name) |
|
1743 |
{ |
|
1744 |
if(_game_mode == GM_EDITOR) |
|
1745 |
sprintf(buf, "%s\\%s.scn", _fios_path, name); |
|
1746 |
else |
|
1747 |
sprintf(buf, "%s\\%s.sav", _fios_path, name); |
|
1748 |
} |
|
1749 |
||
1750 |
void FiosDelete(const char *name) |
|
1751 |
{ |
|
1752 |
char *path = str_buffr; |
|
1753 |
FiosMakeSavegameName(path, name); |
|
1754 |
DeleteFile(path); |
|
1755 |
} |
|
1756 |
||
1757 |
const DriverDesc _video_driver_descs[] = { |
|
1758 |
{"null", "Null Video Driver", &_null_video_driver, 0}, |
|
1759 |
#if defined(WITH_SDL) |
|
1760 |
{"sdl", "SDL Video Driver", &_sdl_video_driver, 1}, |
|
1761 |
#endif |
|
1762 |
{"win32", "Win32 GDI Video Driver", &_win32_video_driver, 2}, |
|
1763 |
{NULL} |
|
1764 |
}; |
|
1765 |
||
1766 |
const DriverDesc _sound_driver_descs[] = { |
|
1767 |
{"null", "Null Sound Driver", &_null_sound_driver, 0}, |
|
1768 |
#if defined(WITH_SDL) |
|
1769 |
{"sdl", "SDL Sound Driver", &_sdl_sound_driver, 1}, |
|
1770 |
#endif |
|
1771 |
{"win32", "Win32 WaveOut Driver", &_win32_sound_driver, 2}, |
|
1772 |
{NULL} |
|
1773 |
}; |
|
1774 |
||
1775 |
const DriverDesc _music_driver_descs[] = { |
|
1776 |
{"null", "Null Music Driver", &_null_music_driver, 0}, |
|
1777 |
#ifdef WIN32_ENABLE_DIRECTMUSIC_SUPPORT |
|
49
8dd4b23924c1
(svn r50) -Fix hopefully the win98 crashes related to music/sound (orudge)
darkvater
parents:
8
diff
changeset
|
1778 |
{"dmusic", "DirectMusic MIDI Driver", &_dmusic_midi_driver, 1}, |
0 | 1779 |
#endif |
49
8dd4b23924c1
(svn r50) -Fix hopefully the win98 crashes related to music/sound (orudge)
darkvater
parents:
8
diff
changeset
|
1780 |
{"win32", "Win32 MIDI Driver", &_win32_music_driver, 2}, |
0 | 1781 |
{NULL} |
1782 |
}; |
|
1783 |
||
1784 |
bool FileExists(const char *filename) |
|
1785 |
{ |
|
1786 |
HANDLE hand = CreateFile(filename, 0, 0, NULL, OPEN_EXISTING, 0, NULL); |
|
1787 |
if (hand == INVALID_HANDLE_VALUE) return false; |
|
1788 |
CloseHandle(hand); |
|
1789 |
return true; |
|
1790 |
} |
|
1791 |
||
1792 |
static int CDECL LanguageCompareFunc(const void *a, const void *b) |
|
1793 |
{ |
|
1794 |
return strcmp(*(char**)a, *(char**)b); |
|
1795 |
} |
|
1796 |
||
1797 |
int GetLanguageList(char **languages, int max) |
|
1798 |
{ |
|
1799 |
HANDLE hand; |
|
1800 |
int num = 0; |
|
1801 |
char filedir[MAX_PATH]; |
|
1802 |
WIN32_FIND_DATA fd; |
|
1803 |
sprintf(filedir, "%s*.lng", _path.lang_dir); |
|
1804 |
||
1805 |
hand = FindFirstFile(filedir, &fd); |
|
1806 |
if (hand != INVALID_HANDLE_VALUE) { |
|
1807 |
do { |
|
1808 |
if (!(fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) { |
|
1809 |
languages[num++] = strdup(fd.cFileName); |
|
1810 |
if (num == max) break; |
|
1811 |
} |
|
1812 |
} while (FindNextFile(hand, &fd)); |
|
1813 |
FindClose(hand); |
|
1814 |
} |
|
1815 |
||
1816 |
qsort(languages, num, sizeof(char*), LanguageCompareFunc); |
|
1817 |
return num; |
|
1818 |
} |
|
1819 |
||
1820 |
static int ParseCommandLine(char *line, char **argv, int max_argc) |
|
1821 |
{ |
|
1822 |
int n = 0; |
|
1823 |
||
1824 |
do { |
|
1825 |
// skip whitespace |
|
1826 |
while (*line == ' ' || *line == '\t') |
|
1827 |
line++; |
|
1828 |
||
1829 |
// end? |
|
1830 |
if (*line == 0) |
|
1831 |
break; |
|
1832 |
||
1833 |
// special handling when quoted |
|
1834 |
if (*line == '"') { |
|
1835 |
argv[n++] = ++line; |
|
1836 |
while (*line != '"') { |
|
1837 |
if (*line == 0) return n; |
|
1838 |
line++; |
|
1839 |
} |
|
1840 |
} else { |
|
1841 |
argv[n++] = line; |
|
1842 |
while (*line != ' ' && *line != '\t') { |
|
1843 |
if (*line == 0) return n; |
|
1844 |
line++; |
|
1845 |
} |
|
1846 |
} |
|
1847 |
*line++ = 0; |
|
1848 |
} while (n != max_argc); |
|
1849 |
||
1850 |
return n; |
|
1851 |
} |
|
1852 |
||
1853 |
||
1854 |
#if defined(_MSC_VER) |
|
1855 |
__int64 _declspec(naked) rdtsc() |
|
1856 |
{ |
|
1857 |
_asm { |
|
1858 |
rdtsc |
|
1859 |
ret |
|
1860 |
} |
|
1861 |
} |
|
1862 |
#endif |
|
1863 |
||
1864 |
void CreateConsole() |
|
1865 |
{ |
|
1866 |
HANDLE hand; |
|
1867 |
CONSOLE_SCREEN_BUFFER_INFO coninfo; |
|
1868 |
||
1869 |
if (_has_console) return; |
|
1870 |
||
1871 |
_has_console = true; |
|
1872 |
||
1873 |
AllocConsole(); |
|
1874 |
||
1875 |
hand = GetStdHandle(STD_OUTPUT_HANDLE); |
|
1876 |
GetConsoleScreenBufferInfo(hand, &coninfo); |
|
1877 |
coninfo.dwSize.Y = 500; |
|
1878 |
SetConsoleScreenBufferSize(hand, coninfo.dwSize); |
|
1879 |
||
1880 |
// redirect unbuffered STDIN, STDOUT, STDERR to the console |
|
1881 |
#if !defined(__CYGWIN__) |
|
1882 |
*stdout = *_fdopen( _open_osfhandle((long)hand, _O_TEXT), "w" ); |
|
1883 |
*stdin = *_fdopen(_open_osfhandle((long)GetStdHandle(STD_INPUT_HANDLE), _O_TEXT), "w" ); |
|
1884 |
*stderr = *_fdopen(_open_osfhandle((long)GetStdHandle(STD_ERROR_HANDLE), _O_TEXT), "w" ); |
|
1885 |
#else |
|
1886 |
// open_osfhandle is not in cygwin |
|
1887 |
*stdout = *fdopen(1, "w" ); |
|
1888 |
*stdin = *fdopen(0, "w" ); |
|
1889 |
*stderr = *fdopen(2, "w" ); |
|
1890 |
#endif |
|
1891 |
||
1892 |
setvbuf( stdin, NULL, _IONBF, 0 ); |
|
1893 |
setvbuf( stdout, NULL, _IONBF, 0 ); |
|
1894 |
setvbuf( stderr, NULL, _IONBF, 0 ); |
|
1895 |
} |
|
1896 |
||
1897 |
void ShowInfo(const char *str) |
|
1898 |
{ |
|
1899 |
if (_has_console) |
|
1900 |
puts(str); |
|
1901 |
else { |
|
1902 |
bool old; |
|
1903 |
||
1904 |
ReleaseCapture(); |
|
1905 |
_left_button_clicked =_left_button_down = false; |
|
1906 |
||
1907 |
old = MyShowCursor(true); |
|
1908 |
if (MessageBoxA(GetActiveWindow(), str, "OpenTTD", MB_ICONINFORMATION | MB_OKCANCEL) == IDCANCEL) { |
|
1909 |
CreateConsole(); |
|
1910 |
} |
|
1911 |
MyShowCursor(old); |
|
1912 |
} |
|
1913 |
} |
|
1914 |
||
1915 |
||
1916 |
int APIENTRY WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPTSTR lpCmdLine,int nCmdShow) |
|
1917 |
{ |
|
1918 |
int argc; |
|
1919 |
char *argv[64]; // max 64 command line arguments |
|
1920 |
_inst = hInstance; |
|
1921 |
||
1922 |
#if defined(_DEBUG) |
|
1923 |
CreateConsole(); |
|
1924 |
#endif |
|
1925 |
||
1926 |
// make sure we have an autosave folder - Done in DeterminePaths |
|
1927 |
// CreateDirectory("autosave", NULL); |
|
1928 |
||
1929 |
// setup random seed to something quite random |
|
1930 |
#if defined(_MSC_VER) |
|
1931 |
{ |
|
1932 |
uint64 seed = rdtsc(); |
|
1933 |
_random_seed_1 = ((uint32*)&seed)[0]; |
|
1934 |
_random_seed_2 = ((uint32*)&seed)[1]; |
|
1935 |
} |
|
1936 |
#else |
|
1937 |
_random_seed_1 = GetTickCount(); |
|
1938 |
_random_seed_2 = _random_seed_1 * 0x1234567; |
|
1939 |
#endif |
|
1940 |
||
1941 |
argc = ParseCommandLine(GetCommandLine(), argv, lengthof(argv)); |
|
1942 |
||
1943 |
#if defined(WIN32_EXCEPTION_TRACKER) |
|
1944 |
{ |
|
1945 |
Win32InitializeExceptions(); |
|
1946 |
} |
|
1947 |
#endif |
|
1948 |
||
1949 |
#if defined(WIN32_EXCEPTION_TRACKER_DEBUG) |
|
1950 |
_try { |
|
1951 |
uint32 _stdcall ExceptionHandler(void *ep); |
|
1952 |
#endif |
|
1953 |
ttd_main(argc, argv); |
|
1954 |
||
1955 |
#if defined(WIN32_EXCEPTION_TRACKER_DEBUG) |
|
1956 |
} _except (ExceptionHandler(_exception_info())) {} |
|
1957 |
#endif |
|
1958 |
||
1959 |
return 0; |
|
1960 |
} |
|
1961 |
||
1962 |
void DeterminePaths() |
|
1963 |
{ |
|
1964 |
char *s; |
|
1965 |
char *cfg; |
|
1966 |
||
1967 |
_path.personal_dir = _path.game_data_dir = cfg = malloc(MAX_PATH); |
|
1968 |
GetCurrentDirectory(MAX_PATH - 1, cfg); |
|
1969 |
||
1970 |
||
1971 |
s = strchr(cfg, 0); |
|
1972 |
if (s[-1] != '\\') { s[0] = '\\'; s[1] = 0; } |
|
1973 |
||
1974 |
_path.save_dir = str_fmt("%ssave", cfg); |
|
1975 |
_path.autosave_dir = str_fmt("%s\\autosave", _path.save_dir); |
|
1976 |
_path.scenario_dir = str_fmt("%sscenario", cfg); |
|
1977 |
_path.gm_dir = str_fmt("%sgm\\", cfg); |
|
1978 |
_path.data_dir = str_fmt("%sdata\\", cfg); |
|
1979 |
_path.lang_dir = str_fmt("%slang\\", cfg); |
|
1980 |
||
1981 |
_config_file = str_fmt("%sopenttd.cfg", _path.personal_dir); |
|
1982 |
||
1983 |
// make (auto)save and scenario folder |
|
1984 |
CreateDirectory(_path.save_dir, NULL); |
|
1985 |
CreateDirectory(_path.autosave_dir, NULL); |
|
1986 |
CreateDirectory(_path.scenario_dir, NULL); |
|
1987 |
} |
|
1988 |