rlm@1
|
1 // System.cpp : Defines the system behaviors for the emulator.
|
rlm@1
|
2 //
|
rlm@1
|
3 #include "stdafx.h"
|
rlm@1
|
4 #include "Sound.h"
|
rlm@1
|
5 #include "Input.h"
|
rlm@1
|
6 #include "IUpdate.h"
|
rlm@1
|
7 #include "ram_search.h"
|
rlm@1
|
8 #include "WinMiscUtil.h"
|
rlm@1
|
9 #include "WinResUtil.h"
|
rlm@1
|
10 #include "resource.h"
|
rlm@1
|
11 #include "VBA.h"
|
rlm@1
|
12 #include "../gba/GBA.h"
|
rlm@1
|
13 #include "../gba/GBAGlobals.h"
|
rlm@1
|
14 #include "../gba/GBASound.h"
|
rlm@1
|
15 #include "../gb/GB.h"
|
rlm@1
|
16 #include "../gb/gbGlobals.h"
|
rlm@1
|
17 //#include "../common/System.h"
|
rlm@1
|
18 #include "../common/movie.h"
|
rlm@1
|
19 #include "../common/vbalua.h"
|
rlm@1
|
20 #include "../common/Text.h"
|
rlm@1
|
21 #include "../common/Util.h"
|
rlm@1
|
22 #include "../common/nesvideos-piece.h"
|
rlm@1
|
23 #include "../version.h"
|
rlm@1
|
24 #include <cassert>
|
rlm@1
|
25
|
rlm@1
|
26 struct EmulatedSystem theEmulator;
|
rlm@1
|
27
|
rlm@1
|
28 u32 RGB_LOW_BITS_MASK = 0;
|
rlm@1
|
29 int emulating = 0;
|
rlm@1
|
30 int systemCartridgeType = 0;
|
rlm@1
|
31 int systemSpeed = 0;
|
rlm@1
|
32 bool systemSoundOn = false;
|
rlm@1
|
33 u32 systemColorMap32[0x10000];
|
rlm@1
|
34 u16 systemColorMap16[0x10000];
|
rlm@1
|
35 u16 systemGbPalette[24];
|
rlm@1
|
36 int systemRedShift = 0;
|
rlm@1
|
37 int systemBlueShift = 0;
|
rlm@1
|
38 int systemGreenShift = 0;
|
rlm@1
|
39 int systemColorDepth = 16;
|
rlm@1
|
40 int systemDebug = 0;
|
rlm@1
|
41 int systemVerbose = 0;
|
rlm@1
|
42 int systemFrameSkip = 0;
|
rlm@1
|
43 int systemSaveUpdateCounter = SYSTEM_SAVE_NOT_UPDATED;
|
rlm@1
|
44
|
rlm@1
|
45 const int32 INITIAL_SENSOR_VALUE = 2047;
|
rlm@1
|
46
|
rlm@1
|
47 int32 sensorX = INITIAL_SENSOR_VALUE;
|
rlm@1
|
48 int32 sensorY = INITIAL_SENSOR_VALUE;
|
rlm@1
|
49 u16 currentButtons [4] = { 0, 0, 0, 0 }; // constrain: never contains hacked buttons, only the lower 16 bits of each are used
|
rlm@1
|
50 u16 lastKeys = 0;
|
rlm@1
|
51
|
rlm@1
|
52 // static_assertion that BUTTON_REGULAR_RECORDING_MASK should be an u16 constant
|
rlm@1
|
53 namespace { const void * const s_STATIC_ASSERTION_(static_cast<void *>(BUTTON_REGULAR_RECORDING_MASK & 0xFFFF0000)); }
|
rlm@1
|
54
|
rlm@1
|
55 #define BMP_BUFFER_MAX_WIDTH (256)
|
rlm@1
|
56 #define BMP_BUFFER_MAX_HEIGHT (224)
|
rlm@1
|
57 #define BMP_BUFFER_MAX_DEPTH (4)
|
rlm@1
|
58 static u8 bmpBuffer[BMP_BUFFER_MAX_WIDTH * BMP_BUFFER_MAX_HEIGHT * BMP_BUFFER_MAX_DEPTH];
|
rlm@1
|
59
|
rlm@1
|
60 static int s_stockThrottleValues[] = {
|
rlm@1
|
61 6, 15, 25, 25, 37, 50, 75, 87, 100, 112, 125, 150, 200, 300, 400, 600, 800, 1000
|
rlm@1
|
62 };
|
rlm@1
|
63
|
rlm@1
|
64 // systemXYZ: Win32 stuff
|
rlm@1
|
65
|
rlm@1
|
66 // input
|
rlm@1
|
67
|
rlm@1
|
68 void systemSetSensorX(int32 x)
|
rlm@1
|
69 {
|
rlm@1
|
70 sensorX = x;
|
rlm@1
|
71 }
|
rlm@1
|
72
|
rlm@1
|
73 void systemSetSensorY(int32 y)
|
rlm@1
|
74 {
|
rlm@1
|
75 sensorY = y;
|
rlm@1
|
76 }
|
rlm@1
|
77
|
rlm@1
|
78 void systemResetSensor()
|
rlm@1
|
79 {
|
rlm@1
|
80 sensorX = sensorY = INITIAL_SENSOR_VALUE;
|
rlm@1
|
81 }
|
rlm@1
|
82
|
rlm@1
|
83 int32 systemGetSensorX()
|
rlm@1
|
84 {
|
rlm@1
|
85 return sensorX;
|
rlm@1
|
86 }
|
rlm@1
|
87
|
rlm@1
|
88 int32 systemGetSensorY()
|
rlm@1
|
89 {
|
rlm@1
|
90 return sensorY;
|
rlm@1
|
91 }
|
rlm@1
|
92
|
rlm@1
|
93 // handles motion sensor input
|
rlm@1
|
94 void systemUpdateMotionSensor(int i)
|
rlm@1
|
95 {
|
rlm@1
|
96 if (i < 0 || i > 3)
|
rlm@1
|
97 i = 0;
|
rlm@1
|
98
|
rlm@1
|
99 if (currentButtons[i] & BUTTON_MASK_LEFT_MOTION)
|
rlm@1
|
100 {
|
rlm@1
|
101 sensorX += 3;
|
rlm@1
|
102 if (sensorX > 2197)
|
rlm@1
|
103 sensorX = 2197;
|
rlm@1
|
104 if (sensorX < 2047)
|
rlm@1
|
105 sensorX = 2057;
|
rlm@1
|
106 }
|
rlm@1
|
107 else if (currentButtons[i] & BUTTON_MASK_RIGHT_MOTION)
|
rlm@1
|
108 {
|
rlm@1
|
109 sensorX -= 3;
|
rlm@1
|
110 if (sensorX < 1897)
|
rlm@1
|
111 sensorX = 1897;
|
rlm@1
|
112 if (sensorX > 2047)
|
rlm@1
|
113 sensorX = 2037;
|
rlm@1
|
114 }
|
rlm@1
|
115 else if (sensorX > 2047)
|
rlm@1
|
116 {
|
rlm@1
|
117 sensorX -= 2;
|
rlm@1
|
118 if (sensorX < 2047)
|
rlm@1
|
119 sensorX = 2047;
|
rlm@1
|
120 }
|
rlm@1
|
121 else
|
rlm@1
|
122 {
|
rlm@1
|
123 sensorX += 2;
|
rlm@1
|
124 if (sensorX > 2047)
|
rlm@1
|
125 sensorX = 2047;
|
rlm@1
|
126 }
|
rlm@1
|
127
|
rlm@1
|
128 if (currentButtons[i] & BUTTON_MASK_UP_MOTION)
|
rlm@1
|
129 {
|
rlm@1
|
130 sensorY += 3;
|
rlm@1
|
131 if (sensorY > 2197)
|
rlm@1
|
132 sensorY = 2197;
|
rlm@1
|
133 if (sensorY < 2047)
|
rlm@1
|
134 sensorY = 2057;
|
rlm@1
|
135 }
|
rlm@1
|
136 else if (currentButtons[i] & BUTTON_MASK_DOWN_MOTION)
|
rlm@1
|
137 {
|
rlm@1
|
138 sensorY -= 3;
|
rlm@1
|
139 if (sensorY < 1897)
|
rlm@1
|
140 sensorY = 1897;
|
rlm@1
|
141 if (sensorY > 2047)
|
rlm@1
|
142 sensorY = 2037;
|
rlm@1
|
143 }
|
rlm@1
|
144 else if (sensorY > 2047)
|
rlm@1
|
145 {
|
rlm@1
|
146 sensorY -= 2;
|
rlm@1
|
147 if (sensorY < 2047)
|
rlm@1
|
148 sensorY = 2047;
|
rlm@1
|
149 }
|
rlm@1
|
150 else
|
rlm@1
|
151 {
|
rlm@1
|
152 sensorY += 2;
|
rlm@1
|
153 if (sensorY > 2047)
|
rlm@1
|
154 sensorY = 2047;
|
rlm@1
|
155 }
|
rlm@1
|
156 }
|
rlm@1
|
157
|
rlm@1
|
158 int systemGetDefaultJoypad()
|
rlm@1
|
159 {
|
rlm@1
|
160 return theApp.joypadDefault;
|
rlm@1
|
161 }
|
rlm@1
|
162
|
rlm@1
|
163 void systemSetDefaultJoypad(int which)
|
rlm@1
|
164 {
|
rlm@1
|
165 theApp.joypadDefault = which;
|
rlm@1
|
166 }
|
rlm@1
|
167
|
rlm@1
|
168 bool systemReadJoypads()
|
rlm@1
|
169 {
|
rlm@1
|
170 // this function is called at every frame, even if vba is fast-forwarded.
|
rlm@1
|
171 // so we try to limit the input frequency here just in case.
|
rlm@1
|
172 static u32 lastTime = systemGetClock();
|
rlm@1
|
173 if ((u32)(systemGetClock() - lastTime) < 10)
|
rlm@1
|
174 return false;
|
rlm@1
|
175 lastTime = systemGetClock();
|
rlm@1
|
176
|
rlm@1
|
177 if (theApp.input)
|
rlm@1
|
178 return theApp.input->readDevices();
|
rlm@1
|
179 return false;
|
rlm@1
|
180 }
|
rlm@1
|
181
|
rlm@1
|
182 u32 systemGetOriginalJoypad(int i, bool sensor)
|
rlm@1
|
183 {
|
rlm@1
|
184 if (i < 0 || i > 3)
|
rlm@1
|
185 i = 0;
|
rlm@1
|
186
|
rlm@1
|
187 u32 res = 0;
|
rlm@1
|
188 if (theApp.input)
|
rlm@1
|
189 res = theApp.input->readDevice(i, sensor);
|
rlm@1
|
190
|
rlm@1
|
191 // +auto input, XOR
|
rlm@1
|
192 // maybe these should be moved into DirectInput.cpp
|
rlm@1
|
193 if (theApp.autoFire || theApp.autoFire2)
|
rlm@1
|
194 {
|
rlm@1
|
195 res ^= (theApp.autoFireToggle ? theApp.autoFire : theApp.autoFire2);
|
rlm@1
|
196 if (!theApp.autofireAccountForLag || !systemCounters.laggedLast)
|
rlm@1
|
197 {
|
rlm@1
|
198 theApp.autoFireToggle = !theApp.autoFireToggle;
|
rlm@1
|
199 }
|
rlm@1
|
200 }
|
rlm@1
|
201 if (theApp.autoHold)
|
rlm@1
|
202 {
|
rlm@1
|
203 res ^= theApp.autoHold;
|
rlm@1
|
204 }
|
rlm@1
|
205
|
rlm@1
|
206 // filter buttons
|
rlm@1
|
207 // maybe better elsewhere?
|
rlm@1
|
208 if (!theApp.allowLeftRight)
|
rlm@1
|
209 {
|
rlm@1
|
210 // disallow L+R or U+D to being pressed at the same time
|
rlm@1
|
211 if ((res & (BUTTON_MASK_RIGHT | BUTTON_MASK_LEFT)) == (BUTTON_MASK_RIGHT | BUTTON_MASK_LEFT))
|
rlm@1
|
212 res &= ~BUTTON_MASK_RIGHT; // leave only LEFT on
|
rlm@1
|
213 if ((res & (BUTTON_MASK_DOWN | BUTTON_MASK_UP)) == (BUTTON_MASK_DOWN | BUTTON_MASK_UP))
|
rlm@1
|
214 res &= ~BUTTON_MASK_DOWN; // leave only UP on
|
rlm@1
|
215 }
|
rlm@1
|
216
|
rlm@1
|
217 if (!sensor)
|
rlm@1
|
218 {
|
rlm@1
|
219 if (res & BUTTON_MOTION_MASK)
|
rlm@1
|
220 res &= ~BUTTON_MOTION_MASK;
|
rlm@1
|
221 }
|
rlm@1
|
222
|
rlm@1
|
223 if (systemCartridgeType != 0 && !gbSgbMode) // regular GB has no L/R buttons
|
rlm@1
|
224 {
|
rlm@1
|
225 if (res & (BUTTON_GBA_ONLY))
|
rlm@1
|
226 res &= ~BUTTON_GBA_ONLY;
|
rlm@1
|
227 }
|
rlm@1
|
228
|
rlm@1
|
229 currentButtons[i] = res & BUTTON_REGULAR_RECORDING_MASK;
|
rlm@1
|
230
|
rlm@1
|
231 return res;
|
rlm@1
|
232 }
|
rlm@1
|
233
|
rlm@1
|
234 u32 systemGetJoypad(int i, bool sensor)
|
rlm@1
|
235 {
|
rlm@1
|
236 if (i < 0 || i > 3)
|
rlm@1
|
237 i = 0;
|
rlm@1
|
238
|
rlm@1
|
239 // input priority: original+auto < Lua < frame search < movie, correct this if wrong
|
rlm@1
|
240
|
rlm@1
|
241 // get original+auto input
|
rlm@1
|
242 u32 hackedButtons = systemGetOriginalJoypad(i, sensor) & BUTTON_NONRECORDINGONLY_MASK;
|
rlm@1
|
243 u32 res = currentButtons[i];
|
rlm@1
|
244
|
rlm@1
|
245 // since movie input has the highest priority, there's no point to read from other input
|
rlm@1
|
246 if (VBAMoviePlaying())
|
rlm@1
|
247 {
|
rlm@1
|
248 // VBAMovieRead() overwrites currentButtons[i]
|
rlm@1
|
249 VBAMovieRead(i, sensor);
|
rlm@1
|
250 res = currentButtons[i];
|
rlm@1
|
251 }
|
rlm@1
|
252 else
|
rlm@1
|
253 {
|
rlm@1
|
254 // Lua input, shouldn't have any side effect within them
|
rlm@1
|
255 if (VBALuaUsingJoypad(i))
|
rlm@1
|
256 res = VBALuaReadJoypad(i);
|
rlm@1
|
257
|
rlm@1
|
258 // override input above
|
rlm@1
|
259 if (theApp.frameSearchSkipping)
|
rlm@1
|
260 res = theApp.frameSearchOldInput[i];
|
rlm@1
|
261
|
rlm@1
|
262 // flush non-hack buttons into the "current buttons" input buffer, which will be read by the movie routine
|
rlm@1
|
263 currentButtons[i] = res & BUTTON_REGULAR_RECORDING_MASK;
|
rlm@1
|
264 VBAMovieWrite(i, sensor);
|
rlm@1
|
265 }
|
rlm@1
|
266
|
rlm@1
|
267 return res | hackedButtons;
|
rlm@1
|
268 }
|
rlm@1
|
269
|
rlm@1
|
270 void systemSetJoypad(int which, u32 buttons)
|
rlm@1
|
271 {
|
rlm@1
|
272 if (which < 0 || which > 3)
|
rlm@1
|
273 which = 0;
|
rlm@1
|
274
|
rlm@1
|
275 currentButtons[which] = buttons;
|
rlm@1
|
276
|
rlm@1
|
277 lastKeys = 0;
|
rlm@1
|
278 }
|
rlm@1
|
279
|
rlm@1
|
280 void systemClearJoypads()
|
rlm@1
|
281 {
|
rlm@1
|
282 for (int i = 0; i < 3; ++i)
|
rlm@1
|
283 currentButtons[i] = 0;
|
rlm@1
|
284
|
rlm@1
|
285 lastKeys = 0;
|
rlm@1
|
286 }
|
rlm@1
|
287
|
rlm@1
|
288 // screen
|
rlm@1
|
289
|
rlm@1
|
290 // delayed repaint
|
rlm@1
|
291 void systemRefreshScreen()
|
rlm@1
|
292 {
|
rlm@1
|
293 if (theApp.m_pMainWnd)
|
rlm@1
|
294 {
|
rlm@1
|
295 theApp.m_pMainWnd->PostMessage(WM_PAINT, NULL, NULL);
|
rlm@1
|
296 }
|
rlm@1
|
297 }
|
rlm@1
|
298
|
rlm@1
|
299 extern bool vbaShuttingDown;
|
rlm@1
|
300
|
rlm@1
|
301 void systemRenderFrame()
|
rlm@1
|
302 {
|
rlm@1
|
303 extern long linearSoundFrameCount;
|
rlm@1
|
304 extern long linearFrameCount;
|
rlm@1
|
305
|
rlm@1
|
306 if (vbaShuttingDown)
|
rlm@1
|
307 return;
|
rlm@1
|
308
|
rlm@1
|
309 ++theApp.renderedFrames;
|
rlm@1
|
310
|
rlm@1
|
311 VBAUpdateFrameCountDisplay();
|
rlm@1
|
312 VBAUpdateButtonPressDisplay();
|
rlm@1
|
313
|
rlm@1
|
314 // "in-game" text rendering
|
rlm@1
|
315 if (textMethod == 0) // transparent text can only be painted once, so timed messages will not be updated
|
rlm@1
|
316 {
|
rlm@1
|
317 extern void DrawLuaGui();
|
rlm@1
|
318 DrawLuaGui();
|
rlm@1
|
319
|
rlm@1
|
320 int copyX = 240, copyY = 160;
|
rlm@1
|
321 if (systemCartridgeType == 1)
|
rlm@1
|
322 if (gbBorderOn)
|
rlm@1
|
323 copyX = 256, copyY = 224;
|
rlm@1
|
324 else
|
rlm@1
|
325 copyX = 160, copyY = 144;
|
rlm@1
|
326 int pitch = copyX * (systemColorDepth / 8) + (systemColorDepth == 24 ? 0 : 4); // FIXME: sure?
|
rlm@1
|
327
|
rlm@1
|
328 DrawTextMessages((u8 *)pix, pitch, 0, copyY);
|
rlm@1
|
329 }
|
rlm@1
|
330
|
rlm@1
|
331 ++linearFrameCount;
|
rlm@1
|
332 if (!theApp.sound)
|
rlm@1
|
333 {
|
rlm@1
|
334 if (linearFrameCount > 10000)
|
rlm@1
|
335 linearFrameCount -= 10000;
|
rlm@1
|
336 linearSoundFrameCount = linearFrameCount;
|
rlm@1
|
337 }
|
rlm@1
|
338
|
rlm@1
|
339 // record avi
|
rlm@1
|
340 int width = 240;
|
rlm@1
|
341 int height = 160;
|
rlm@1
|
342 switch (systemCartridgeType)
|
rlm@1
|
343 {
|
rlm@1
|
344 case 0:
|
rlm@1
|
345 width = 240;
|
rlm@1
|
346 height = 160;
|
rlm@1
|
347 break;
|
rlm@1
|
348 case 1:
|
rlm@1
|
349 if (gbBorderOn)
|
rlm@1
|
350 {
|
rlm@1
|
351 width = 256;
|
rlm@1
|
352 height = 224;
|
rlm@1
|
353 }
|
rlm@1
|
354 else
|
rlm@1
|
355 {
|
rlm@1
|
356 width = 160;
|
rlm@1
|
357 height = 144;
|
rlm@1
|
358 }
|
rlm@1
|
359 break;
|
rlm@1
|
360 }
|
rlm@1
|
361
|
rlm@1
|
362 bool firstFrameLogged = false;
|
rlm@1
|
363 --linearFrameCount;
|
rlm@1
|
364 do
|
rlm@1
|
365 {
|
rlm@1
|
366 ++linearFrameCount;
|
rlm@1
|
367
|
rlm@1
|
368 if (theApp.aviRecording && (!theApp.altAviRecordMethod || (theApp.altAviRecordMethod && !firstFrameLogged)))
|
rlm@1
|
369 {
|
rlm@1
|
370 // usually aviRecorder is created when vba starts avi recording, though
|
rlm@1
|
371 if (theApp.aviRecorder == NULL)
|
rlm@1
|
372 {
|
rlm@1
|
373 theApp.aviRecorder = new AVIWrite();
|
rlm@1
|
374
|
rlm@1
|
375 theApp.aviRecorder->SetFPS(60);
|
rlm@1
|
376
|
rlm@1
|
377 BITMAPINFOHEADER bi;
|
rlm@1
|
378 memset(&bi, 0, sizeof(bi));
|
rlm@1
|
379 bi.biSize = 0x28;
|
rlm@1
|
380 bi.biPlanes = 1;
|
rlm@1
|
381 bi.biBitCount = 24;
|
rlm@1
|
382 bi.biWidth = width;
|
rlm@1
|
383 bi.biHeight = height;
|
rlm@1
|
384 bi.biSizeImage = 3 * width * height;
|
rlm@1
|
385 theApp.aviRecorder->SetVideoFormat(&bi);
|
rlm@1
|
386 if (!theApp.aviRecorder->Open(theApp.aviRecordName))
|
rlm@1
|
387 {
|
rlm@1
|
388 delete theApp.aviRecorder;
|
rlm@1
|
389 theApp.aviRecorder = NULL;
|
rlm@1
|
390 theApp.aviRecording = false;
|
rlm@1
|
391 }
|
rlm@1
|
392 }
|
rlm@1
|
393
|
rlm@1
|
394 if (theApp.aviRecorder != NULL && !theApp.aviRecorder->IsPaused())
|
rlm@1
|
395 {
|
rlm@1
|
396 assert(
|
rlm@1
|
397 width <= BMP_BUFFER_MAX_WIDTH && height <= BMP_BUFFER_MAX_HEIGHT && systemColorDepth <=
|
rlm@1
|
398 BMP_BUFFER_MAX_DEPTH * 8);
|
rlm@1
|
399 utilWriteBMP(bmpBuffer, width, height, systemColorDepth, pix);
|
rlm@1
|
400 theApp.aviRecorder->AddFrame(bmpBuffer);
|
rlm@1
|
401 }
|
rlm@1
|
402 }
|
rlm@1
|
403
|
rlm@1
|
404 if (theApp.nvVideoLog)
|
rlm@1
|
405 {
|
rlm@1
|
406 // convert from whatever bit depth to 16-bit, while stripping away extra pixels
|
rlm@1
|
407 assert(width <= BMP_BUFFER_MAX_WIDTH && height <= BMP_BUFFER_MAX_HEIGHT && 16 <= BMP_BUFFER_MAX_DEPTH * 8);
|
rlm@1
|
408 utilWriteBMP(bmpBuffer, width, -height, 16, pix);
|
rlm@1
|
409 NESVideoLoggingVideo((u8 *)bmpBuffer, width, height, 0x1000000 * 60);
|
rlm@1
|
410 }
|
rlm@1
|
411
|
rlm@1
|
412 firstFrameLogged = true;
|
rlm@1
|
413 }
|
rlm@1
|
414 while (linearFrameCount < linearSoundFrameCount); // compensate for frames lost due to frame skip being nonzero, etc.
|
rlm@1
|
415
|
rlm@1
|
416 if (textMethod != 0) // do not draw Lua HUD to a video dump
|
rlm@1
|
417 {
|
rlm@1
|
418 extern void DrawLuaGui();
|
rlm@1
|
419 DrawLuaGui();
|
rlm@1
|
420 }
|
rlm@1
|
421
|
rlm@1
|
422 // interframe blending
|
rlm@1
|
423 if (theApp.ifbFunction)
|
rlm@1
|
424 {
|
rlm@1
|
425 if (systemColorDepth == 16)
|
rlm@1
|
426 theApp.ifbFunction(pix + theApp.filterWidth * 2 + 4, theApp.filterWidth * 2 + 4,
|
rlm@1
|
427 theApp.filterWidth, theApp.filterHeight);
|
rlm@1
|
428 else
|
rlm@1
|
429 theApp.ifbFunction(pix + theApp.filterWidth * 4 + 4, theApp.filterWidth * 4 + 4,
|
rlm@1
|
430 theApp.filterWidth, theApp.filterHeight);
|
rlm@1
|
431 }
|
rlm@1
|
432
|
rlm@1
|
433 systemRedrawScreen();
|
rlm@1
|
434 }
|
rlm@1
|
435
|
rlm@1
|
436 void systemRedrawScreen()
|
rlm@1
|
437 {
|
rlm@1
|
438 if (vbaShuttingDown)
|
rlm@1
|
439 return;
|
rlm@1
|
440
|
rlm@1
|
441 if (theApp.display)
|
rlm@1
|
442 theApp.display->render();
|
rlm@1
|
443
|
rlm@1
|
444 systemUpdateListeners();
|
rlm@1
|
445 }
|
rlm@1
|
446
|
rlm@1
|
447 void systemUpdateListeners()
|
rlm@1
|
448 {
|
rlm@1
|
449 if (vbaShuttingDown)
|
rlm@1
|
450 return;
|
rlm@1
|
451
|
rlm@1
|
452 Update_RAM_Search(); // updates RAM search and RAM watch
|
rlm@1
|
453
|
rlm@1
|
454 // update viewers etc.
|
rlm@1
|
455 if (theApp.updateCount)
|
rlm@1
|
456 {
|
rlm@1
|
457 POSITION pos = theApp.updateList.GetHeadPosition();
|
rlm@1
|
458 while (pos)
|
rlm@1
|
459 {
|
rlm@1
|
460 IUpdateListener *up = theApp.updateList.GetNext(pos);
|
rlm@1
|
461 if (up)
|
rlm@1
|
462 up->update();
|
rlm@1
|
463 }
|
rlm@1
|
464 }
|
rlm@1
|
465 }
|
rlm@1
|
466
|
rlm@1
|
467 int systemScreenCapture(int captureNumber)
|
rlm@1
|
468 {
|
rlm@1
|
469 return winScreenCapture(captureNumber);
|
rlm@1
|
470 }
|
rlm@1
|
471
|
rlm@1
|
472 void systemMessage(int number, const char *defaultMsg, ...)
|
rlm@1
|
473 {
|
rlm@1
|
474 CString buffer;
|
rlm@1
|
475 va_list valist;
|
rlm@1
|
476 CString msg = defaultMsg;
|
rlm@1
|
477 if (number)
|
rlm@1
|
478 msg = winResLoadString(number);
|
rlm@1
|
479
|
rlm@1
|
480 va_start(valist, defaultMsg);
|
rlm@1
|
481 buffer.FormatV(msg, valist);
|
rlm@1
|
482
|
rlm@1
|
483 theApp.winCheckFullscreen();
|
rlm@1
|
484 systemSoundClearBuffer();
|
rlm@1
|
485 AfxGetApp()->m_pMainWnd->MessageBox(buffer, winResLoadString(IDS_ERROR), MB_OK | MB_ICONERROR);
|
rlm@1
|
486
|
rlm@1
|
487 va_end(valist);
|
rlm@1
|
488 }
|
rlm@1
|
489
|
rlm@1
|
490 void systemScreenMessage(const char *msg, int slot, int duration, const char *colorList)
|
rlm@1
|
491 {
|
rlm@1
|
492 if (slot < 0 || slot > SCREEN_MESSAGE_SLOTS)
|
rlm@1
|
493 return;
|
rlm@1
|
494
|
rlm@1
|
495 theApp.screenMessage[slot] = true;
|
rlm@1
|
496 theApp.screenMessageTime[slot] = GetTickCount();
|
rlm@1
|
497 theApp.screenMessageDuration[slot] = duration;
|
rlm@1
|
498 theApp.screenMessageBuffer[slot] = msg;
|
rlm@1
|
499 theApp.screenMessageColorBuffer[slot] = colorList ? colorList : "";
|
rlm@1
|
500
|
rlm@1
|
501 if (theApp.screenMessageBuffer[slot].GetLength() > 40)
|
rlm@1
|
502 theApp.screenMessageBuffer[slot] = theApp.screenMessageBuffer[slot].Left(40);
|
rlm@1
|
503
|
rlm@1
|
504 // update the display when a main slot message appears while the game is paused
|
rlm@1
|
505 if (slot == 0 && (theApp.paused || (theApp.frameSearching)))
|
rlm@1
|
506 systemRefreshScreen();
|
rlm@1
|
507 }
|
rlm@1
|
508
|
rlm@1
|
509 void systemShowSpeed(int speed)
|
rlm@1
|
510 {
|
rlm@1
|
511 systemSpeed = speed;
|
rlm@1
|
512 theApp.showRenderedFrames = theApp.renderedFrames;
|
rlm@1
|
513 theApp.renderedFrames = 0;
|
rlm@1
|
514 if (theApp.videoOption <= VIDEO_4X && theApp.showSpeed)
|
rlm@1
|
515 {
|
rlm@1
|
516 CString buffer;
|
rlm@1
|
517 if (theApp.showSpeed == 1)
|
rlm@1
|
518 buffer.Format(VBA_NAME_AND_VERSION " %3d%%", systemSpeed);
|
rlm@1
|
519 else
|
rlm@1
|
520 buffer.Format(VBA_NAME_AND_VERSION " %3d%% (%d fps | %d skipped)",
|
rlm@1
|
521 systemSpeed,
|
rlm@1
|
522 theApp.showRenderedFrames,
|
rlm@1
|
523 systemFrameSkip);
|
rlm@1
|
524
|
rlm@1
|
525 systemSetTitle(buffer);
|
rlm@1
|
526 }
|
rlm@1
|
527 }
|
rlm@1
|
528
|
rlm@1
|
529 void systemSetTitle(const char *title)
|
rlm@1
|
530 {
|
rlm@1
|
531 if (theApp.m_pMainWnd != NULL)
|
rlm@1
|
532 {
|
rlm@1
|
533 AfxGetApp()->m_pMainWnd->SetWindowText(title);
|
rlm@1
|
534 }
|
rlm@1
|
535 }
|
rlm@1
|
536
|
rlm@1
|
537 // timing/speed
|
rlm@1
|
538
|
rlm@1
|
539 u32 systemGetClock()
|
rlm@1
|
540 {
|
rlm@1
|
541 return timeGetTime();
|
rlm@1
|
542 }
|
rlm@1
|
543
|
rlm@1
|
544 void systemIncreaseThrottle()
|
rlm@1
|
545 {
|
rlm@1
|
546 int throttle = theApp.throttle;
|
rlm@1
|
547
|
rlm@1
|
548 if (throttle < 6)
|
rlm@1
|
549 ++throttle;
|
rlm@1
|
550 else if (throttle < s_stockThrottleValues[_countof(s_stockThrottleValues) - 1])
|
rlm@1
|
551 {
|
rlm@1
|
552 int i = 0;
|
rlm@1
|
553 while (throttle >= s_stockThrottleValues[i])
|
rlm@1
|
554 {
|
rlm@1
|
555 ++i;
|
rlm@1
|
556 }
|
rlm@1
|
557 throttle = s_stockThrottleValues[i];
|
rlm@1
|
558 }
|
rlm@1
|
559
|
rlm@1
|
560 systemSetThrottle(throttle);
|
rlm@1
|
561 }
|
rlm@1
|
562
|
rlm@1
|
563 void systemDecreaseThrottle()
|
rlm@1
|
564 {
|
rlm@1
|
565 int throttle = theApp.throttle;
|
rlm@1
|
566
|
rlm@1
|
567 if (throttle > 6)
|
rlm@1
|
568 {
|
rlm@1
|
569 int i = _countof(s_stockThrottleValues) - 1;
|
rlm@1
|
570 while (throttle <= s_stockThrottleValues[i])
|
rlm@1
|
571 {
|
rlm@1
|
572 --i;
|
rlm@1
|
573 }
|
rlm@1
|
574 throttle = s_stockThrottleValues[i];
|
rlm@1
|
575 }
|
rlm@1
|
576 else if (throttle > 1)
|
rlm@1
|
577 --throttle;
|
rlm@1
|
578
|
rlm@1
|
579 systemSetThrottle(throttle);
|
rlm@1
|
580 }
|
rlm@1
|
581
|
rlm@1
|
582 void systemSetThrottle(int throttle)
|
rlm@1
|
583 {
|
rlm@1
|
584 theApp.throttle = throttle;
|
rlm@1
|
585 char str[256];
|
rlm@1
|
586 sprintf(str, "%d%% throttle speed", theApp.throttle);
|
rlm@1
|
587 systemScreenMessage(str);
|
rlm@1
|
588 }
|
rlm@1
|
589
|
rlm@1
|
590 int systemGetThrottle()
|
rlm@1
|
591 {
|
rlm@1
|
592 return theApp.throttle;
|
rlm@1
|
593 }
|
rlm@1
|
594
|
rlm@1
|
595 void systemFrame()
|
rlm@1
|
596 {
|
rlm@1
|
597 if (theApp.altAviRecordMethod && theApp.aviRecording)
|
rlm@1
|
598 {
|
rlm@1
|
599 if (theApp.aviRecorder)
|
rlm@1
|
600 {
|
rlm@1
|
601 if (!theApp.aviRecorder->IsSoundAdded())
|
rlm@1
|
602 {
|
rlm@1
|
603 WAVEFORMATEX wfx;
|
rlm@1
|
604 memset(&wfx, 0, sizeof(wfx));
|
rlm@1
|
605 wfx.wFormatTag = WAVE_FORMAT_PCM;
|
rlm@1
|
606 wfx.nChannels = 2;
|
rlm@1
|
607 wfx.nSamplesPerSec = 44100 / soundQuality;
|
rlm@1
|
608 wfx.wBitsPerSample = 16;
|
rlm@1
|
609 wfx.nBlockAlign = (wfx.wBitsPerSample / 8) * wfx.nChannels;
|
rlm@1
|
610 wfx.nAvgBytesPerSec = wfx.nSamplesPerSec * wfx.nBlockAlign;
|
rlm@1
|
611 wfx.cbSize = 0;
|
rlm@1
|
612 theApp.aviRecorder->SetSoundFormat(&wfx);
|
rlm@1
|
613 }
|
rlm@1
|
614 theApp.aviRecorder->AddSound((u8 *)soundFrameSound, soundFrameSoundWritten * 2);
|
rlm@1
|
615 }
|
rlm@1
|
616 }
|
rlm@1
|
617
|
rlm@1
|
618 soundFrameSoundWritten = 0;
|
rlm@1
|
619
|
rlm@1
|
620 // no more stupid updates :)
|
rlm@1
|
621
|
rlm@1
|
622 extern int quitAfterTime; // from VBA.cpp
|
rlm@1
|
623 void VBAMovieStop(bool8 suppress_message); // from ../movie.cpp
|
rlm@1
|
624 if (quitAfterTime >= 0 && systemCounters.frameCount == quitAfterTime)
|
rlm@1
|
625 {
|
rlm@1
|
626 VBAMovieStop(true);
|
rlm@1
|
627 AfxPostQuitMessage(0);
|
rlm@1
|
628 }
|
rlm@1
|
629
|
rlm@1
|
630 // change the sound speed, or set it to normal - must always do this or it won't get reset after a change, but that's OK
|
rlm@1
|
631 // because it's inexpensive
|
rlm@1
|
632 if (theApp.sound)
|
rlm@1
|
633 {
|
rlm@1
|
634 theApp.sound->setSpeed(
|
rlm@1
|
635 speedup || theApp.winPauseNextFrame || !synchronize || theApp.accuratePitchThrottle || theApp.useOldSync
|
rlm@1
|
636 ? 1.0f : (float)theApp.throttle / 100.0f);
|
rlm@1
|
637 }
|
rlm@1
|
638
|
rlm@1
|
639 // if a throttle speed is set and we're not allowed to change the sound frequency to achieve it,
|
rlm@1
|
640 // sleep for a certain amount each time we get here to approximate the necessary slowdown
|
rlm@1
|
641 if (synchronize && (theApp.accuratePitchThrottle || !theApp.sound || theApp.throttle < 6) /*&& !theApp.winPauseNextFrame*/)
|
rlm@1
|
642 {
|
rlm@1
|
643 /// FIXME: this is still a horrible way of achieving a certain frame time
|
rlm@1
|
644 /// (look at what Snes9x does - it's complicated but much much better)
|
rlm@1
|
645
|
rlm@1
|
646 static float sleepAmt = 0.0f; // variable to smooth out the sleeping amount so it doesn't oscillate so fast
|
rlm@1
|
647 // if(!theApp.wasPaused) {
|
rlm@1
|
648 if (!speedup)
|
rlm@1
|
649 {
|
rlm@1
|
650 u32 time = systemGetClock();
|
rlm@1
|
651 u32 diff = time - theApp.throttleLastTime;
|
rlm@1
|
652 if (theApp.wasPaused)
|
rlm@1
|
653 diff = 0;
|
rlm@1
|
654
|
rlm@1
|
655 int target = (100000 / (60 * theApp.throttle));
|
rlm@1
|
656 int d = (target - diff);
|
rlm@1
|
657
|
rlm@1
|
658 if (d > 1000) // added to avoid 500-day waits for vba to start emulating.
|
rlm@1
|
659 d = 1000; // I suspect most users aren't that patient, and would find 1 second to be a more reasonable delay.
|
rlm@1
|
660
|
rlm@1
|
661 sleepAmt = 0.8f * sleepAmt + 0.2f * (float)d;
|
rlm@1
|
662 if (d - sleepAmt <= 1.5f && d - sleepAmt >= -1.5f)
|
rlm@1
|
663 d = (int)(sleepAmt);
|
rlm@1
|
664
|
rlm@1
|
665 if (d > 0)
|
rlm@1
|
666 {
|
rlm@1
|
667 Sleep(d);
|
rlm@1
|
668 }
|
rlm@1
|
669 }
|
rlm@1
|
670 theApp.throttleLastTime = systemGetClock();
|
rlm@1
|
671 //}
|
rlm@1
|
672 //else
|
rlm@1
|
673 //{
|
rlm@1
|
674 // Sleep(100);
|
rlm@1
|
675 //}
|
rlm@1
|
676 }
|
rlm@1
|
677
|
rlm@1
|
678 if (systemCounters.frameCount % 10 == 0)
|
rlm@1
|
679 {
|
rlm@1
|
680 if (theApp.rewindMemory)
|
rlm@1
|
681 {
|
rlm@1
|
682 if (++theApp.rewindCounter >= (theApp.rewindTimer))
|
rlm@1
|
683 {
|
rlm@1
|
684 theApp.rewindSaveNeeded = true;
|
rlm@1
|
685 theApp.rewindCounter = 0;
|
rlm@1
|
686 }
|
rlm@1
|
687 }
|
rlm@1
|
688 if (systemSaveUpdateCounter)
|
rlm@1
|
689 {
|
rlm@1
|
690 if (--systemSaveUpdateCounter <= SYSTEM_SAVE_NOT_UPDATED)
|
rlm@1
|
691 {
|
rlm@1
|
692 winWriteBatteryFile();
|
rlm@1
|
693 systemSaveUpdateCounter = SYSTEM_SAVE_NOT_UPDATED;
|
rlm@1
|
694 }
|
rlm@1
|
695 }
|
rlm@1
|
696 }
|
rlm@1
|
697
|
rlm@1
|
698 theApp.wasPaused = false;
|
rlm@1
|
699 /// theApp.autoFrameSkipLastTime = time;
|
rlm@1
|
700 }
|
rlm@1
|
701
|
rlm@1
|
702 int systemFramesToSkip()
|
rlm@1
|
703 {
|
rlm@1
|
704 int framesToSkip = systemFrameSkip;
|
rlm@1
|
705
|
rlm@1
|
706 bool fastForward = speedup;
|
rlm@1
|
707
|
rlm@1
|
708 #if (defined(WIN32) && !defined(SDL))
|
rlm@1
|
709 fastForward = (fastForward || theApp.frameSearchSkipping);
|
rlm@1
|
710 int throttle = theApp.throttle;
|
rlm@1
|
711 if (theApp.frameSearching && throttle < 100)
|
rlm@1
|
712 throttle = 100;
|
rlm@1
|
713 #else
|
rlm@1
|
714 extern int throttle;
|
rlm@1
|
715 #endif
|
rlm@1
|
716
|
rlm@1
|
717 #if (defined(WIN32) && !defined(SDL))
|
rlm@1
|
718 if (theApp.aviRecording || theApp.nvVideoLog)
|
rlm@1
|
719 {
|
rlm@1
|
720 framesToSkip = 0; // render all frames
|
rlm@1
|
721 }
|
rlm@1
|
722 else
|
rlm@1
|
723 {
|
rlm@1
|
724 if (fastForward)
|
rlm@1
|
725 framesToSkip = 9; // try 6 FPS during speedup
|
rlm@1
|
726 else if (throttle != 100)
|
rlm@1
|
727 framesToSkip = (framesToSkip * throttle) / 100;
|
rlm@1
|
728 }
|
rlm@1
|
729 #endif
|
rlm@1
|
730
|
rlm@1
|
731 return framesToSkip;
|
rlm@1
|
732 }
|
rlm@1
|
733
|
rlm@1
|
734 // sound
|
rlm@1
|
735
|
rlm@1
|
736 bool systemSoundInit()
|
rlm@1
|
737 {
|
rlm@1
|
738 if (theApp.sound)
|
rlm@1
|
739 delete theApp.sound;
|
rlm@1
|
740
|
rlm@1
|
741 extern ISound *newDirectSound();
|
rlm@1
|
742 theApp.sound = newDirectSound();
|
rlm@1
|
743 return theApp.sound->init();
|
rlm@1
|
744 }
|
rlm@1
|
745
|
rlm@1
|
746 void systemSoundShutdown()
|
rlm@1
|
747 {
|
rlm@1
|
748 if (theApp.sound)
|
rlm@1
|
749 delete theApp.sound;
|
rlm@1
|
750 theApp.sound = NULL;
|
rlm@1
|
751 }
|
rlm@1
|
752
|
rlm@1
|
753 void systemSoundPause()
|
rlm@1
|
754 {
|
rlm@1
|
755 if (theApp.sound)
|
rlm@1
|
756 theApp.sound->pause();
|
rlm@1
|
757 soundPaused = 1;
|
rlm@1
|
758 }
|
rlm@1
|
759
|
rlm@1
|
760 void systemSoundResume()
|
rlm@1
|
761 {
|
rlm@1
|
762 if (theApp.sound)
|
rlm@1
|
763 theApp.sound->resume();
|
rlm@1
|
764 soundPaused = 0;
|
rlm@1
|
765 }
|
rlm@1
|
766
|
rlm@1
|
767 bool systemSoundIsPaused()
|
rlm@1
|
768 {
|
rlm@1
|
769 // return soundPaused;
|
rlm@1
|
770 return !(theApp.sound && theApp.sound->isPlaying());
|
rlm@1
|
771 }
|
rlm@1
|
772
|
rlm@1
|
773 void systemSoundClearBuffer()
|
rlm@1
|
774 {
|
rlm@1
|
775 if (theApp.sound)
|
rlm@1
|
776 theApp.sound->clearAudioBuffer();
|
rlm@1
|
777 }
|
rlm@1
|
778
|
rlm@1
|
779 void systemSoundReset()
|
rlm@1
|
780 {
|
rlm@1
|
781 if (theApp.sound)
|
rlm@1
|
782 theApp.sound->reset();
|
rlm@1
|
783 }
|
rlm@1
|
784
|
rlm@1
|
785 void systemSoundWriteToBuffer()
|
rlm@1
|
786 {
|
rlm@1
|
787 if (theApp.sound)
|
rlm@1
|
788 theApp.sound->write();
|
rlm@1
|
789 }
|
rlm@1
|
790
|
rlm@1
|
791 bool systemSoundCanChangeQuality()
|
rlm@1
|
792 {
|
rlm@1
|
793 return true;
|
rlm@1
|
794 }
|
rlm@1
|
795
|
rlm@1
|
796 bool systemSoundSetQuality(int quality)
|
rlm@1
|
797 {
|
rlm@1
|
798 if (systemCartridgeType == 0)
|
rlm@1
|
799 soundSetQuality(quality);
|
rlm@1
|
800 else
|
rlm@1
|
801 gbSoundSetQuality(quality);
|
rlm@1
|
802
|
rlm@1
|
803 return true;
|
rlm@1
|
804 }
|
rlm@1
|
805
|
rlm@1
|
806 // emulation
|
rlm@1
|
807
|
rlm@1
|
808 bool systemIsEmulating()
|
rlm@1
|
809 {
|
rlm@1
|
810 return emulating != 0;
|
rlm@1
|
811 }
|
rlm@1
|
812
|
rlm@1
|
813 void systemGbBorderOn()
|
rlm@1
|
814 {
|
rlm@1
|
815 if (vbaShuttingDown)
|
rlm@1
|
816 return;
|
rlm@1
|
817
|
rlm@1
|
818 if (emulating && systemCartridgeType == 1)
|
rlm@1
|
819 {
|
rlm@1
|
820 theApp.updateWindowSize(theApp.videoOption);
|
rlm@1
|
821 }
|
rlm@1
|
822 }
|
rlm@1
|
823
|
rlm@1
|
824 bool systemIsRunningGBA()
|
rlm@1
|
825 {
|
rlm@1
|
826 return (systemCartridgeType == 0);
|
rlm@1
|
827 }
|
rlm@1
|
828
|
rlm@1
|
829 bool systemIsSpedUp()
|
rlm@1
|
830 {
|
rlm@1
|
831 return theApp.speedupToggle;
|
rlm@1
|
832 }
|
rlm@1
|
833
|
rlm@1
|
834 bool systemIsPaused()
|
rlm@1
|
835 {
|
rlm@1
|
836 return theApp.paused;
|
rlm@1
|
837 }
|
rlm@1
|
838
|
rlm@1
|
839 void systemSetPause(bool pause)
|
rlm@1
|
840 {
|
rlm@1
|
841 if (pause)
|
rlm@1
|
842 {
|
rlm@1
|
843 capturePrevious = false;
|
rlm@1
|
844 theApp.wasPaused = true;
|
rlm@1
|
845 theApp.paused = true;
|
rlm@1
|
846 theApp.speedupToggle = false;
|
rlm@1
|
847 theApp.winPauseNextFrame = false;
|
rlm@1
|
848 systemSoundPause();
|
rlm@1
|
849 systemRefreshScreen();;
|
rlm@1
|
850 }
|
rlm@1
|
851 else
|
rlm@1
|
852 {
|
rlm@1
|
853 theApp.paused = false;
|
rlm@1
|
854 systemSoundResume();
|
rlm@1
|
855 }
|
rlm@1
|
856 }
|
rlm@1
|
857
|
rlm@1
|
858 // aka. frame advance
|
rlm@1
|
859 bool systemPauseOnFrame()
|
rlm@1
|
860 {
|
rlm@1
|
861 if (theApp.winPauseNextFrame)
|
rlm@1
|
862 {
|
rlm@1
|
863 if (!theApp.nextframeAccountForLag || !systemCounters.laggedLast)
|
rlm@1
|
864 {
|
rlm@1
|
865 theApp.winPauseNextFrame = false;
|
rlm@1
|
866 return true;
|
rlm@1
|
867 }
|
rlm@1
|
868 }
|
rlm@1
|
869
|
rlm@1
|
870 return false;
|
rlm@1
|
871 }
|
rlm@1
|
872
|
rlm@1
|
873 bool systemLoadBIOS(const char *biosFileName, bool useBiosFile)
|
rlm@1
|
874 {
|
rlm@1
|
875 bool use = false;
|
rlm@1
|
876 if (systemCartridgeType == 0)
|
rlm@1
|
877 use = CPULoadBios(biosFileName, useBiosFile);
|
rlm@1
|
878 else
|
rlm@1
|
879 use = false;
|
rlm@1
|
880 return use;
|
rlm@1
|
881 }
|
rlm@1
|
882
|
rlm@1
|
883 // FIXME: now platform-independant stuff
|
rlm@1
|
884 // it should be admitted that the naming schema/code organization is a whole mess
|
rlm@1
|
885 // these things should be moved somewhere else
|
rlm@1
|
886
|
rlm@1
|
887 EmulatedSystemCounters systemCounters =
|
rlm@1
|
888 {
|
rlm@1
|
889 // frameCount
|
rlm@1
|
890 0,
|
rlm@1
|
891 // lagCount
|
rlm@1
|
892 0,
|
rlm@1
|
893 // extraCount
|
rlm@1
|
894 0,
|
rlm@1
|
895 // lagged
|
rlm@1
|
896 true,
|
rlm@1
|
897 // laggedLast
|
rlm@1
|
898 true,
|
rlm@1
|
899 };
|
rlm@1
|
900
|
rlm@1
|
901 // VBAxyz stuff are not part of the core.
|
rlm@1
|
902
|
rlm@1
|
903 void VBAOnEnteringFrameBoundary()
|
rlm@1
|
904 {
|
rlm@1
|
905 CallRegisteredLuaFunctions(LUACALL_AFTEREMULATION);
|
rlm@1
|
906
|
rlm@1
|
907 if (VBALuaRunning())
|
rlm@1
|
908 {
|
rlm@1
|
909 VBALuaFrameBoundary();
|
rlm@1
|
910 }
|
rlm@1
|
911
|
rlm@1
|
912 VBAMovieUpdateState();
|
rlm@1
|
913 }
|
rlm@1
|
914
|
rlm@1
|
915 void VBAOnExitingFrameBoundary()
|
rlm@1
|
916 {
|
rlm@1
|
917 ;
|
rlm@1
|
918 }
|
rlm@1
|
919
|
rlm@1
|
920 //////////////////////////////////////////////
|
rlm@1
|
921 // ultility
|
rlm@1
|
922
|
rlm@1
|
923 extern void toolsLog(const char *);
|
rlm@1
|
924
|
rlm@1
|
925 void log(const char *msg, ...)
|
rlm@1
|
926 {
|
rlm@1
|
927 CString buffer;
|
rlm@1
|
928 va_list valist;
|
rlm@1
|
929
|
rlm@1
|
930 va_start(valist, msg);
|
rlm@1
|
931 buffer.FormatV(msg, valist);
|
rlm@1
|
932
|
rlm@1
|
933 toolsLog(buffer);
|
rlm@1
|
934
|
rlm@1
|
935 va_end(valist);
|
rlm@1
|
936 }
|