Mercurial > vba-clojure
view src/win32/System.cpp @ 6:458a4f18f3cd
working on lua generation
author | Robert McIntyre <rlm@mit.edu> |
---|---|
date | Sat, 03 Mar 2012 10:48:33 -0600 |
parents | f9f4f1b99eed |
children |
line wrap: on
line source
1 // System.cpp : Defines the system behaviors for the emulator.2 //3 #include "stdafx.h"4 #include "Sound.h"5 #include "Input.h"6 #include "IUpdate.h"7 #include "ram_search.h"8 #include "WinMiscUtil.h"9 #include "WinResUtil.h"10 #include "resource.h"11 #include "VBA.h"12 #include "../gba/GBA.h"13 #include "../gba/GBAGlobals.h"14 #include "../gba/GBASound.h"15 #include "../gb/GB.h"16 #include "../gb/gbGlobals.h"17 //#include "../common/System.h"18 #include "../common/movie.h"19 #include "../common/vbalua.h"20 #include "../common/Text.h"21 #include "../common/Util.h"22 #include "../common/nesvideos-piece.h"23 #include "../version.h"24 #include <cassert>26 struct EmulatedSystem theEmulator;28 u32 RGB_LOW_BITS_MASK = 0;29 int emulating = 0;30 int systemCartridgeType = 0;31 int systemSpeed = 0;32 bool systemSoundOn = false;33 u32 systemColorMap32[0x10000];34 u16 systemColorMap16[0x10000];35 u16 systemGbPalette[24];36 int systemRedShift = 0;37 int systemBlueShift = 0;38 int systemGreenShift = 0;39 int systemColorDepth = 16;40 int systemDebug = 0;41 int systemVerbose = 0;42 int systemFrameSkip = 0;43 int systemSaveUpdateCounter = SYSTEM_SAVE_NOT_UPDATED;45 const int32 INITIAL_SENSOR_VALUE = 2047;47 int32 sensorX = INITIAL_SENSOR_VALUE;48 int32 sensorY = INITIAL_SENSOR_VALUE;49 u16 currentButtons [4] = { 0, 0, 0, 0 }; // constrain: never contains hacked buttons, only the lower 16 bits of each are used50 u16 lastKeys = 0;52 // static_assertion that BUTTON_REGULAR_RECORDING_MASK should be an u16 constant53 namespace { const void * const s_STATIC_ASSERTION_(static_cast<void *>(BUTTON_REGULAR_RECORDING_MASK & 0xFFFF0000)); }55 #define BMP_BUFFER_MAX_WIDTH (256)56 #define BMP_BUFFER_MAX_HEIGHT (224)57 #define BMP_BUFFER_MAX_DEPTH (4)58 static u8 bmpBuffer[BMP_BUFFER_MAX_WIDTH * BMP_BUFFER_MAX_HEIGHT * BMP_BUFFER_MAX_DEPTH];60 static int s_stockThrottleValues[] = {61 6, 15, 25, 25, 37, 50, 75, 87, 100, 112, 125, 150, 200, 300, 400, 600, 800, 100062 };64 // systemXYZ: Win32 stuff66 // input68 void systemSetSensorX(int32 x)69 {70 sensorX = x;71 }73 void systemSetSensorY(int32 y)74 {75 sensorY = y;76 }78 void systemResetSensor()79 {80 sensorX = sensorY = INITIAL_SENSOR_VALUE;81 }83 int32 systemGetSensorX()84 {85 return sensorX;86 }88 int32 systemGetSensorY()89 {90 return sensorY;91 }93 // handles motion sensor input94 void systemUpdateMotionSensor(int i)95 {96 if (i < 0 || i > 3)97 i = 0;99 if (currentButtons[i] & BUTTON_MASK_LEFT_MOTION)100 {101 sensorX += 3;102 if (sensorX > 2197)103 sensorX = 2197;104 if (sensorX < 2047)105 sensorX = 2057;106 }107 else if (currentButtons[i] & BUTTON_MASK_RIGHT_MOTION)108 {109 sensorX -= 3;110 if (sensorX < 1897)111 sensorX = 1897;112 if (sensorX > 2047)113 sensorX = 2037;114 }115 else if (sensorX > 2047)116 {117 sensorX -= 2;118 if (sensorX < 2047)119 sensorX = 2047;120 }121 else122 {123 sensorX += 2;124 if (sensorX > 2047)125 sensorX = 2047;126 }128 if (currentButtons[i] & BUTTON_MASK_UP_MOTION)129 {130 sensorY += 3;131 if (sensorY > 2197)132 sensorY = 2197;133 if (sensorY < 2047)134 sensorY = 2057;135 }136 else if (currentButtons[i] & BUTTON_MASK_DOWN_MOTION)137 {138 sensorY -= 3;139 if (sensorY < 1897)140 sensorY = 1897;141 if (sensorY > 2047)142 sensorY = 2037;143 }144 else if (sensorY > 2047)145 {146 sensorY -= 2;147 if (sensorY < 2047)148 sensorY = 2047;149 }150 else151 {152 sensorY += 2;153 if (sensorY > 2047)154 sensorY = 2047;155 }156 }158 int systemGetDefaultJoypad()159 {160 return theApp.joypadDefault;161 }163 void systemSetDefaultJoypad(int which)164 {165 theApp.joypadDefault = which;166 }168 bool systemReadJoypads()169 {170 // this function is called at every frame, even if vba is fast-forwarded.171 // so we try to limit the input frequency here just in case.172 static u32 lastTime = systemGetClock();173 if ((u32)(systemGetClock() - lastTime) < 10)174 return false;175 lastTime = systemGetClock();177 if (theApp.input)178 return theApp.input->readDevices();179 return false;180 }182 u32 systemGetOriginalJoypad(int i, bool sensor)183 {184 if (i < 0 || i > 3)185 i = 0;187 u32 res = 0;188 if (theApp.input)189 res = theApp.input->readDevice(i, sensor);191 // +auto input, XOR192 // maybe these should be moved into DirectInput.cpp193 if (theApp.autoFire || theApp.autoFire2)194 {195 res ^= (theApp.autoFireToggle ? theApp.autoFire : theApp.autoFire2);196 if (!theApp.autofireAccountForLag || !systemCounters.laggedLast)197 {198 theApp.autoFireToggle = !theApp.autoFireToggle;199 }200 }201 if (theApp.autoHold)202 {203 res ^= theApp.autoHold;204 }206 // filter buttons207 // maybe better elsewhere?208 if (!theApp.allowLeftRight)209 {210 // disallow L+R or U+D to being pressed at the same time211 if ((res & (BUTTON_MASK_RIGHT | BUTTON_MASK_LEFT)) == (BUTTON_MASK_RIGHT | BUTTON_MASK_LEFT))212 res &= ~BUTTON_MASK_RIGHT; // leave only LEFT on213 if ((res & (BUTTON_MASK_DOWN | BUTTON_MASK_UP)) == (BUTTON_MASK_DOWN | BUTTON_MASK_UP))214 res &= ~BUTTON_MASK_DOWN; // leave only UP on215 }217 if (!sensor)218 {219 if (res & BUTTON_MOTION_MASK)220 res &= ~BUTTON_MOTION_MASK;221 }223 if (systemCartridgeType != 0 && !gbSgbMode) // regular GB has no L/R buttons224 {225 if (res & (BUTTON_GBA_ONLY))226 res &= ~BUTTON_GBA_ONLY;227 }229 currentButtons[i] = res & BUTTON_REGULAR_RECORDING_MASK;231 return res;232 }234 u32 systemGetJoypad(int i, bool sensor)235 {236 if (i < 0 || i > 3)237 i = 0;239 // input priority: original+auto < Lua < frame search < movie, correct this if wrong241 // get original+auto input242 u32 hackedButtons = systemGetOriginalJoypad(i, sensor) & BUTTON_NONRECORDINGONLY_MASK;243 u32 res = currentButtons[i];245 // since movie input has the highest priority, there's no point to read from other input246 if (VBAMoviePlaying())247 {248 // VBAMovieRead() overwrites currentButtons[i]249 VBAMovieRead(i, sensor);250 res = currentButtons[i];251 }252 else253 {254 // Lua input, shouldn't have any side effect within them255 if (VBALuaUsingJoypad(i))256 res = VBALuaReadJoypad(i);258 // override input above259 if (theApp.frameSearchSkipping)260 res = theApp.frameSearchOldInput[i];262 // flush non-hack buttons into the "current buttons" input buffer, which will be read by the movie routine263 currentButtons[i] = res & BUTTON_REGULAR_RECORDING_MASK;264 VBAMovieWrite(i, sensor);265 }267 return res | hackedButtons;268 }270 void systemSetJoypad(int which, u32 buttons)271 {272 if (which < 0 || which > 3)273 which = 0;275 currentButtons[which] = buttons;277 lastKeys = 0;278 }280 void systemClearJoypads()281 {282 for (int i = 0; i < 3; ++i)283 currentButtons[i] = 0;285 lastKeys = 0;286 }288 // screen290 // delayed repaint291 void systemRefreshScreen()292 {293 if (theApp.m_pMainWnd)294 {295 theApp.m_pMainWnd->PostMessage(WM_PAINT, NULL, NULL);296 }297 }299 extern bool vbaShuttingDown;301 void systemRenderFrame()302 {303 extern long linearSoundFrameCount;304 extern long linearFrameCount;306 if (vbaShuttingDown)307 return;309 ++theApp.renderedFrames;311 VBAUpdateFrameCountDisplay();312 VBAUpdateButtonPressDisplay();314 // "in-game" text rendering315 if (textMethod == 0) // transparent text can only be painted once, so timed messages will not be updated316 {317 extern void DrawLuaGui();318 DrawLuaGui();320 int copyX = 240, copyY = 160;321 if (systemCartridgeType == 1)322 if (gbBorderOn)323 copyX = 256, copyY = 224;324 else325 copyX = 160, copyY = 144;326 int pitch = copyX * (systemColorDepth / 8) + (systemColorDepth == 24 ? 0 : 4); // FIXME: sure?328 DrawTextMessages((u8 *)pix, pitch, 0, copyY);329 }331 ++linearFrameCount;332 if (!theApp.sound)333 {334 if (linearFrameCount > 10000)335 linearFrameCount -= 10000;336 linearSoundFrameCount = linearFrameCount;337 }339 // record avi340 int width = 240;341 int height = 160;342 switch (systemCartridgeType)343 {344 case 0:345 width = 240;346 height = 160;347 break;348 case 1:349 if (gbBorderOn)350 {351 width = 256;352 height = 224;353 }354 else355 {356 width = 160;357 height = 144;358 }359 break;360 }362 bool firstFrameLogged = false;363 --linearFrameCount;364 do365 {366 ++linearFrameCount;368 if (theApp.aviRecording && (!theApp.altAviRecordMethod || (theApp.altAviRecordMethod && !firstFrameLogged)))369 {370 // usually aviRecorder is created when vba starts avi recording, though371 if (theApp.aviRecorder == NULL)372 {373 theApp.aviRecorder = new AVIWrite();375 theApp.aviRecorder->SetFPS(60);377 BITMAPINFOHEADER bi;378 memset(&bi, 0, sizeof(bi));379 bi.biSize = 0x28;380 bi.biPlanes = 1;381 bi.biBitCount = 24;382 bi.biWidth = width;383 bi.biHeight = height;384 bi.biSizeImage = 3 * width * height;385 theApp.aviRecorder->SetVideoFormat(&bi);386 if (!theApp.aviRecorder->Open(theApp.aviRecordName))387 {388 delete theApp.aviRecorder;389 theApp.aviRecorder = NULL;390 theApp.aviRecording = false;391 }392 }394 if (theApp.aviRecorder != NULL && !theApp.aviRecorder->IsPaused())395 {396 assert(397 width <= BMP_BUFFER_MAX_WIDTH && height <= BMP_BUFFER_MAX_HEIGHT && systemColorDepth <=398 BMP_BUFFER_MAX_DEPTH * 8);399 utilWriteBMP(bmpBuffer, width, height, systemColorDepth, pix);400 theApp.aviRecorder->AddFrame(bmpBuffer);401 }402 }404 if (theApp.nvVideoLog)405 {406 // convert from whatever bit depth to 16-bit, while stripping away extra pixels407 assert(width <= BMP_BUFFER_MAX_WIDTH && height <= BMP_BUFFER_MAX_HEIGHT && 16 <= BMP_BUFFER_MAX_DEPTH * 8);408 utilWriteBMP(bmpBuffer, width, -height, 16, pix);409 NESVideoLoggingVideo((u8 *)bmpBuffer, width, height, 0x1000000 * 60);410 }412 firstFrameLogged = true;413 }414 while (linearFrameCount < linearSoundFrameCount); // compensate for frames lost due to frame skip being nonzero, etc.416 if (textMethod != 0) // do not draw Lua HUD to a video dump417 {418 extern void DrawLuaGui();419 DrawLuaGui();420 }422 // interframe blending423 if (theApp.ifbFunction)424 {425 if (systemColorDepth == 16)426 theApp.ifbFunction(pix + theApp.filterWidth * 2 + 4, theApp.filterWidth * 2 + 4,427 theApp.filterWidth, theApp.filterHeight);428 else429 theApp.ifbFunction(pix + theApp.filterWidth * 4 + 4, theApp.filterWidth * 4 + 4,430 theApp.filterWidth, theApp.filterHeight);431 }433 systemRedrawScreen();434 }436 void systemRedrawScreen()437 {438 if (vbaShuttingDown)439 return;441 if (theApp.display)442 theApp.display->render();444 systemUpdateListeners();445 }447 void systemUpdateListeners()448 {449 if (vbaShuttingDown)450 return;452 Update_RAM_Search(); // updates RAM search and RAM watch454 // update viewers etc.455 if (theApp.updateCount)456 {457 POSITION pos = theApp.updateList.GetHeadPosition();458 while (pos)459 {460 IUpdateListener *up = theApp.updateList.GetNext(pos);461 if (up)462 up->update();463 }464 }465 }467 int systemScreenCapture(int captureNumber)468 {469 return winScreenCapture(captureNumber);470 }472 void systemMessage(int number, const char *defaultMsg, ...)473 {474 CString buffer;475 va_list valist;476 CString msg = defaultMsg;477 if (number)478 msg = winResLoadString(number);480 va_start(valist, defaultMsg);481 buffer.FormatV(msg, valist);483 theApp.winCheckFullscreen();484 systemSoundClearBuffer();485 AfxGetApp()->m_pMainWnd->MessageBox(buffer, winResLoadString(IDS_ERROR), MB_OK | MB_ICONERROR);487 va_end(valist);488 }490 void systemScreenMessage(const char *msg, int slot, int duration, const char *colorList)491 {492 if (slot < 0 || slot > SCREEN_MESSAGE_SLOTS)493 return;495 theApp.screenMessage[slot] = true;496 theApp.screenMessageTime[slot] = GetTickCount();497 theApp.screenMessageDuration[slot] = duration;498 theApp.screenMessageBuffer[slot] = msg;499 theApp.screenMessageColorBuffer[slot] = colorList ? colorList : "";501 if (theApp.screenMessageBuffer[slot].GetLength() > 40)502 theApp.screenMessageBuffer[slot] = theApp.screenMessageBuffer[slot].Left(40);504 // update the display when a main slot message appears while the game is paused505 if (slot == 0 && (theApp.paused || (theApp.frameSearching)))506 systemRefreshScreen();507 }509 void systemShowSpeed(int speed)510 {511 systemSpeed = speed;512 theApp.showRenderedFrames = theApp.renderedFrames;513 theApp.renderedFrames = 0;514 if (theApp.videoOption <= VIDEO_4X && theApp.showSpeed)515 {516 CString buffer;517 if (theApp.showSpeed == 1)518 buffer.Format(VBA_NAME_AND_VERSION " %3d%%", systemSpeed);519 else520 buffer.Format(VBA_NAME_AND_VERSION " %3d%% (%d fps | %d skipped)",521 systemSpeed,522 theApp.showRenderedFrames,523 systemFrameSkip);525 systemSetTitle(buffer);526 }527 }529 void systemSetTitle(const char *title)530 {531 if (theApp.m_pMainWnd != NULL)532 {533 AfxGetApp()->m_pMainWnd->SetWindowText(title);534 }535 }537 // timing/speed539 u32 systemGetClock()540 {541 return timeGetTime();542 }544 void systemIncreaseThrottle()545 {546 int throttle = theApp.throttle;548 if (throttle < 6)549 ++throttle;550 else if (throttle < s_stockThrottleValues[_countof(s_stockThrottleValues) - 1])551 {552 int i = 0;553 while (throttle >= s_stockThrottleValues[i])554 {555 ++i;556 }557 throttle = s_stockThrottleValues[i];558 }560 systemSetThrottle(throttle);561 }563 void systemDecreaseThrottle()564 {565 int throttle = theApp.throttle;567 if (throttle > 6)568 {569 int i = _countof(s_stockThrottleValues) - 1;570 while (throttle <= s_stockThrottleValues[i])571 {572 --i;573 }574 throttle = s_stockThrottleValues[i];575 }576 else if (throttle > 1)577 --throttle;579 systemSetThrottle(throttle);580 }582 void systemSetThrottle(int throttle)583 {584 theApp.throttle = throttle;585 char str[256];586 sprintf(str, "%d%% throttle speed", theApp.throttle);587 systemScreenMessage(str);588 }590 int systemGetThrottle()591 {592 return theApp.throttle;593 }595 void systemFrame()596 {597 if (theApp.altAviRecordMethod && theApp.aviRecording)598 {599 if (theApp.aviRecorder)600 {601 if (!theApp.aviRecorder->IsSoundAdded())602 {603 WAVEFORMATEX wfx;604 memset(&wfx, 0, sizeof(wfx));605 wfx.wFormatTag = WAVE_FORMAT_PCM;606 wfx.nChannels = 2;607 wfx.nSamplesPerSec = 44100 / soundQuality;608 wfx.wBitsPerSample = 16;609 wfx.nBlockAlign = (wfx.wBitsPerSample / 8) * wfx.nChannels;610 wfx.nAvgBytesPerSec = wfx.nSamplesPerSec * wfx.nBlockAlign;611 wfx.cbSize = 0;612 theApp.aviRecorder->SetSoundFormat(&wfx);613 }614 theApp.aviRecorder->AddSound((u8 *)soundFrameSound, soundFrameSoundWritten * 2);615 }616 }618 soundFrameSoundWritten = 0;620 // no more stupid updates :)622 extern int quitAfterTime; // from VBA.cpp623 void VBAMovieStop(bool8 suppress_message); // from ../movie.cpp624 if (quitAfterTime >= 0 && systemCounters.frameCount == quitAfterTime)625 {626 VBAMovieStop(true);627 AfxPostQuitMessage(0);628 }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 OK631 // because it's inexpensive632 if (theApp.sound)633 {634 theApp.sound->setSpeed(635 speedup || theApp.winPauseNextFrame || !synchronize || theApp.accuratePitchThrottle || theApp.useOldSync636 ? 1.0f : (float)theApp.throttle / 100.0f);637 }639 // if a throttle speed is set and we're not allowed to change the sound frequency to achieve it,640 // sleep for a certain amount each time we get here to approximate the necessary slowdown641 if (synchronize && (theApp.accuratePitchThrottle || !theApp.sound || theApp.throttle < 6) /*&& !theApp.winPauseNextFrame*/)642 {643 /// FIXME: this is still a horrible way of achieving a certain frame time644 /// (look at what Snes9x does - it's complicated but much much better)646 static float sleepAmt = 0.0f; // variable to smooth out the sleeping amount so it doesn't oscillate so fast647 // if(!theApp.wasPaused) {648 if (!speedup)649 {650 u32 time = systemGetClock();651 u32 diff = time - theApp.throttleLastTime;652 if (theApp.wasPaused)653 diff = 0;655 int target = (100000 / (60 * theApp.throttle));656 int d = (target - diff);658 if (d > 1000) // added to avoid 500-day waits for vba to start emulating.659 d = 1000; // I suspect most users aren't that patient, and would find 1 second to be a more reasonable delay.661 sleepAmt = 0.8f * sleepAmt + 0.2f * (float)d;662 if (d - sleepAmt <= 1.5f && d - sleepAmt >= -1.5f)663 d = (int)(sleepAmt);665 if (d > 0)666 {667 Sleep(d);668 }669 }670 theApp.throttleLastTime = systemGetClock();671 //}672 //else673 //{674 // Sleep(100);675 //}676 }678 if (systemCounters.frameCount % 10 == 0)679 {680 if (theApp.rewindMemory)681 {682 if (++theApp.rewindCounter >= (theApp.rewindTimer))683 {684 theApp.rewindSaveNeeded = true;685 theApp.rewindCounter = 0;686 }687 }688 if (systemSaveUpdateCounter)689 {690 if (--systemSaveUpdateCounter <= SYSTEM_SAVE_NOT_UPDATED)691 {692 winWriteBatteryFile();693 systemSaveUpdateCounter = SYSTEM_SAVE_NOT_UPDATED;694 }695 }696 }698 theApp.wasPaused = false;699 /// theApp.autoFrameSkipLastTime = time;700 }702 int systemFramesToSkip()703 {704 int framesToSkip = systemFrameSkip;706 bool fastForward = speedup;708 #if (defined(WIN32) && !defined(SDL))709 fastForward = (fastForward || theApp.frameSearchSkipping);710 int throttle = theApp.throttle;711 if (theApp.frameSearching && throttle < 100)712 throttle = 100;713 #else714 extern int throttle;715 #endif717 #if (defined(WIN32) && !defined(SDL))718 if (theApp.aviRecording || theApp.nvVideoLog)719 {720 framesToSkip = 0; // render all frames721 }722 else723 {724 if (fastForward)725 framesToSkip = 9; // try 6 FPS during speedup726 else if (throttle != 100)727 framesToSkip = (framesToSkip * throttle) / 100;728 }729 #endif731 return framesToSkip;732 }734 // sound736 bool systemSoundInit()737 {738 if (theApp.sound)739 delete theApp.sound;741 extern ISound *newDirectSound();742 theApp.sound = newDirectSound();743 return theApp.sound->init();744 }746 void systemSoundShutdown()747 {748 if (theApp.sound)749 delete theApp.sound;750 theApp.sound = NULL;751 }753 void systemSoundPause()754 {755 if (theApp.sound)756 theApp.sound->pause();757 soundPaused = 1;758 }760 void systemSoundResume()761 {762 if (theApp.sound)763 theApp.sound->resume();764 soundPaused = 0;765 }767 bool systemSoundIsPaused()768 {769 // return soundPaused;770 return !(theApp.sound && theApp.sound->isPlaying());771 }773 void systemSoundClearBuffer()774 {775 if (theApp.sound)776 theApp.sound->clearAudioBuffer();777 }779 void systemSoundReset()780 {781 if (theApp.sound)782 theApp.sound->reset();783 }785 void systemSoundWriteToBuffer()786 {787 if (theApp.sound)788 theApp.sound->write();789 }791 bool systemSoundCanChangeQuality()792 {793 return true;794 }796 bool systemSoundSetQuality(int quality)797 {798 if (systemCartridgeType == 0)799 soundSetQuality(quality);800 else801 gbSoundSetQuality(quality);803 return true;804 }806 // emulation808 bool systemIsEmulating()809 {810 return emulating != 0;811 }813 void systemGbBorderOn()814 {815 if (vbaShuttingDown)816 return;818 if (emulating && systemCartridgeType == 1)819 {820 theApp.updateWindowSize(theApp.videoOption);821 }822 }824 bool systemIsRunningGBA()825 {826 return (systemCartridgeType == 0);827 }829 bool systemIsSpedUp()830 {831 return theApp.speedupToggle;832 }834 bool systemIsPaused()835 {836 return theApp.paused;837 }839 void systemSetPause(bool pause)840 {841 if (pause)842 {843 capturePrevious = false;844 theApp.wasPaused = true;845 theApp.paused = true;846 theApp.speedupToggle = false;847 theApp.winPauseNextFrame = false;848 systemSoundPause();849 systemRefreshScreen();;850 }851 else852 {853 theApp.paused = false;854 systemSoundResume();855 }856 }858 // aka. frame advance859 bool systemPauseOnFrame()860 {861 if (theApp.winPauseNextFrame)862 {863 if (!theApp.nextframeAccountForLag || !systemCounters.laggedLast)864 {865 theApp.winPauseNextFrame = false;866 return true;867 }868 }870 return false;871 }873 bool systemLoadBIOS(const char *biosFileName, bool useBiosFile)874 {875 bool use = false;876 if (systemCartridgeType == 0)877 use = CPULoadBios(biosFileName, useBiosFile);878 else879 use = false;880 return use;881 }883 // FIXME: now platform-independant stuff884 // it should be admitted that the naming schema/code organization is a whole mess885 // these things should be moved somewhere else887 EmulatedSystemCounters systemCounters =888 {889 // frameCount890 0,891 // lagCount892 0,893 // extraCount894 0,895 // lagged896 true,897 // laggedLast898 true,899 };901 // VBAxyz stuff are not part of the core.903 void VBAOnEnteringFrameBoundary()904 {905 CallRegisteredLuaFunctions(LUACALL_AFTEREMULATION);907 if (VBALuaRunning())908 {909 VBALuaFrameBoundary();910 }912 VBAMovieUpdateState();913 }915 void VBAOnExitingFrameBoundary()916 {917 ;918 }920 //////////////////////////////////////////////921 // ultility923 extern void toolsLog(const char *);925 void log(const char *msg, ...)926 {927 CString buffer;928 va_list valist;930 va_start(valist, msg);931 buffer.FormatV(msg, valist);933 toolsLog(buffer);935 va_end(valist);936 }